mirror of
https://github.com/Infisical/infisical.git
synced 2025-07-31 10:38:12 +00:00
Compare commits
346 Commits
daniel/go-
...
daniel/fix
Author | SHA1 | Date | |
---|---|---|---|
|
53983d13f3 | ||
|
5d9e47aec6 | ||
|
be968be813 | ||
|
dc60d59e2e | ||
|
e3f48e72b0 | ||
|
3c6b7aee9a | ||
|
a183e94ff4 | ||
|
b54e780443 | ||
|
5376bb72b3 | ||
|
56d0d59ddc | ||
|
ef9d4a4eee | ||
|
873c6eea18 | ||
|
8d8e0bb794 | ||
|
348cf1c50c | ||
|
05669efdd8 | ||
|
c302630551 | ||
|
2a4c9100be | ||
|
9ced5717ac | ||
|
b2f2541d0b | ||
|
3a9ad8d306 | ||
|
10207d03dd | ||
|
832dd62158 | ||
|
df29f3499f | ||
|
0d4c05f537 | ||
|
fb0407fec8 | ||
|
7d899463b4 | ||
|
cfaf076352 | ||
|
a875489172 | ||
|
8634f8348b | ||
|
ad5b16d448 | ||
|
62e6acb7dc | ||
|
bf50eed8b0 | ||
|
dcd69b5d99 | ||
|
dda98a0036 | ||
|
24ab66f61f | ||
|
9b97afad1c | ||
|
dca3832fd4 | ||
|
c3458a9d34 | ||
|
76d371f13c | ||
|
7e9bcc5ce1 | ||
|
029f2fa3af | ||
|
0e9b2a8045 | ||
|
514cf07ba5 | ||
|
55efc58566 | ||
|
9b03f4984a | ||
|
a04f938b6c | ||
|
596a22e9eb | ||
|
2ea01537c0 | ||
|
ab57c658a8 | ||
|
89030c92c7 | ||
|
d842aef714 | ||
|
53089e26b9 | ||
|
be890b4189 | ||
|
cd0f126cf2 | ||
|
a092b9a19f | ||
|
2f043849c9 | ||
|
d15fa9f176 | ||
|
61508ec90a | ||
|
9d84bfa69e | ||
|
6a1d465778 | ||
|
5eff705486 | ||
|
cd532bc20d | ||
|
18cdaaf024 | ||
|
74e1dbdf9b | ||
|
64ab75748c | ||
|
f7b689158d | ||
|
19b9a31f0b | ||
|
0568cdcec6 | ||
|
a4bc459576 | ||
|
b0b73acc21 | ||
|
07d66cbb65 | ||
|
ee97782860 | ||
|
c856de534b | ||
|
eefd71f4cc | ||
|
77e9609d0c | ||
|
afbbe5b7ba | ||
|
54d5cdedab | ||
|
9e12935a9f | ||
|
101fa56d83 | ||
|
9bceb99110 | ||
|
ca7a0a73be | ||
|
3632361f3c | ||
|
f5c0274844 | ||
|
36a11387dd | ||
|
a82c94472a | ||
|
508f9610ca | ||
|
59065c0648 | ||
|
6443c94283 | ||
|
26611881bc | ||
|
2852989ac1 | ||
|
124bb7c205 | ||
|
697445cb1f | ||
|
04108907ba | ||
|
411cac2a31 | ||
|
afb9920fca | ||
|
ccf99d2465 | ||
|
bca84f74c5 | ||
|
6c93973db7 | ||
|
8d3f8c94fb | ||
|
2eeb7dbc41 | ||
|
f18624d2e4 | ||
|
42a49da17b | ||
|
5d87ce866c | ||
|
02d7f90ec2 | ||
|
03564fc59b | ||
|
8669f5c39a | ||
|
c2bd2e6963 | ||
|
eb23d114a2 | ||
|
dec2cd465b | ||
|
4cdec49751 | ||
|
43967ef848 | ||
|
55046d4144 | ||
|
124acfd279 | ||
|
62e12269b8 | ||
|
f03d8b718e | ||
|
acf13df0f3 | ||
|
cb8ec57177 | ||
|
b543f2ce50 | ||
|
f852e629ef | ||
|
58b74d97bb | ||
|
ba12aab65a | ||
|
952c4a3931 | ||
|
4a1bae07ca | ||
|
c24f72435a | ||
|
4bf378c28d | ||
|
407c8e17d3 | ||
|
67b7fb819a | ||
|
edfccb2ae2 | ||
|
df8dc43bcf | ||
|
0d610f2644 | ||
|
a422d211fe | ||
|
f66d5e3d28 | ||
|
2c4e951fe2 | ||
|
e23d2dff64 | ||
|
e7de6ad5d9 | ||
|
ca0d79d664 | ||
|
adc0552df0 | ||
|
cff79e7c8c | ||
|
450e653005 | ||
|
c866e55d1b | ||
|
d66c2a85f4 | ||
|
3b8c0a5cb1 | ||
|
b77f0fed45 | ||
|
e8bc47b573 | ||
|
c6785eff3a | ||
|
bc1a9055ee | ||
|
dbe1f2bcff | ||
|
15107ebfaa | ||
|
435a395a15 | ||
|
fe829af054 | ||
|
bd9dc44a69 | ||
|
765dd84d19 | ||
|
ac100e17f4 | ||
|
e349f9aa3b | ||
|
29c3c41ebb | ||
|
e4af0759b8 | ||
|
c681774709 | ||
|
f63f2d9c69 | ||
|
044662901a | ||
|
8cdb2082d9 | ||
|
52d0f5e1be | ||
|
be1e7be0d5 | ||
|
e1b0bc1b97 | ||
|
f05d1b9d95 | ||
|
fa2bd6a75e | ||
|
2402ce2a12 | ||
|
f770a18d41 | ||
|
8ab7470f74 | ||
|
eb56c23db1 | ||
|
14812adade | ||
|
99b1efffc7 | ||
|
af6189c82b | ||
|
b6ca18af5d | ||
|
ee7bb6d60d | ||
|
bfde867ba7 | ||
|
1a20f3148c | ||
|
ce5b14222f | ||
|
74a43d55f7 | ||
|
85cce4274e | ||
|
9eb88836e9 | ||
|
d6c9658747 | ||
|
f9967c0cc8 | ||
|
bd8dfe4089 | ||
|
03fcaadab2 | ||
|
d3a0a84815 | ||
|
49ae146470 | ||
|
f73b362c84 | ||
|
d9043fa9e0 | ||
|
98f6dc8df9 | ||
|
12c67d921d | ||
|
7dea2ba916 | ||
|
ace27a3605 | ||
|
e85ea1a458 | ||
|
fb16464fda | ||
|
c6b636bb42 | ||
|
034ac68b58 | ||
|
33e2c52f14 | ||
|
b435a06a92 | ||
|
48c23db3f9 | ||
|
3159972ec3 | ||
|
8a5c293a6e | ||
|
1d9c18d155 | ||
|
13945bb31d | ||
|
9df9197cac | ||
|
3809729e31 | ||
|
03d29a4afc | ||
|
a4264335fe | ||
|
7752bab0f0 | ||
|
56a20dc397 | ||
|
6f79d8bb6c | ||
|
044ac01100 | ||
|
641c0308f9 | ||
|
ecfb833797 | ||
|
256f14cf6a | ||
|
32c28227b2 | ||
|
3be6402727 | ||
|
90c09c64cb | ||
|
d0da69b999 | ||
|
7fb3730b22 | ||
|
49e154ddd1 | ||
|
3742976bcb | ||
|
5695137f24 | ||
|
13d7cfd41b | ||
|
81fc5d3c18 | ||
|
8e8f44895d | ||
|
45570490a0 | ||
|
1add5d6a24 | ||
|
7ac0536236 | ||
|
89e9f46ae5 | ||
|
e3728b8a61 | ||
|
92bbabde3c | ||
|
11b4c5381a | ||
|
97496c1b3c | ||
|
3cac1acf08 | ||
|
c3756b8cc0 | ||
|
8678c79c02 | ||
|
d2f010d17d | ||
|
5c8d5e8430 | ||
|
7c8d99875a | ||
|
ab30b0803f | ||
|
e2d68f07d1 | ||
|
07ced66538 | ||
|
9cb0ec231b | ||
|
8b169b2b9e | ||
|
b9c02264c7 | ||
|
9f96a9d188 | ||
|
55f232a642 | ||
|
34ff65d09c | ||
|
fe38c79f68 | ||
|
a8aecc378b | ||
|
9ce7161aea | ||
|
1951ca723c | ||
|
416f85f7e2 | ||
|
75bef6fc8b | ||
|
5fa6e8bcf2 | ||
|
f4a5d9c391 | ||
|
3c6b976d8f | ||
|
787d2287a0 | ||
|
92f73d66f0 | ||
|
3cd8670064 | ||
|
4e3dd15d67 | ||
|
4c97ba1221 | ||
|
b89128fb32 | ||
|
c788c0cb80 | ||
|
e3fde17622 | ||
|
2eb9f30ef5 | ||
|
9432f3ce4a | ||
|
5393afbd05 | ||
|
cb304d9a10 | ||
|
a5f29db670 | ||
|
009b49685c | ||
|
90d3f4d643 | ||
|
cc08d31300 | ||
|
71deb7c62a | ||
|
efec1c0a96 | ||
|
efa4b7a4b6 | ||
|
65e0077d6c | ||
|
1be311ffd9 | ||
|
3994962d0b | ||
|
0b9334f34c | ||
|
2b4396547d | ||
|
761ec8dcc0 | ||
|
56e69bc5e9 | ||
|
067faef6a2 | ||
|
026b934a87 | ||
|
90eef0495e | ||
|
119fe97b14 | ||
|
dab3daee86 | ||
|
f2e344c11d | ||
|
df1a879e73 | ||
|
fb21d4e13d | ||
|
ca6f50a257 | ||
|
26f0adbf7e | ||
|
456d9ca5ce | ||
|
e652fd962c | ||
|
bc16484f3f | ||
|
4e87cc7c28 | ||
|
d9e2b99338 | ||
|
9bac996c7a | ||
|
089d57ea59 | ||
|
14a17d638d | ||
|
5d9755b332 | ||
|
4f6b73518e | ||
|
2f4965659c | ||
|
2dc12693b0 | ||
|
5305139a55 | ||
|
db72c07e81 | ||
|
2f3ae5429a | ||
|
56e216c37c | ||
|
8db3544885 | ||
|
f196c6a0ce | ||
|
246eecc23c | ||
|
013b744706 | ||
|
fe68328aeb | ||
|
1dcfd14431 | ||
|
f232f00f77 | ||
|
82517477cb | ||
|
4e149cce81 | ||
|
5894cb4049 | ||
|
4938dda303 | ||
|
62112447a6 | ||
|
bead911e0f | ||
|
49987ca1e5 | ||
|
cec083aa9b | ||
|
9146079317 | ||
|
243ffc9904 | ||
|
b07d29faa2 | ||
|
60fcc42d8c | ||
|
fabf7181fa | ||
|
c9a7b6abb6 | ||
|
3c53befb3e | ||
|
1f0cf6cc9b | ||
|
84c19a7554 | ||
|
d3ea91c54b | ||
|
ecb58b8680 | ||
|
1972a3c6ed | ||
|
a0b1fb23df | ||
|
5910c11d88 | ||
|
a768496c5e | ||
|
e59cc138d9 | ||
|
7a7d41ca83 | ||
|
bd8b56a224 | ||
|
aa5bd117e6 | ||
|
e66e6a7490 | ||
|
e54f499026 | ||
|
7a5e0e9463 |
@@ -74,21 +74,21 @@ jobs:
|
|||||||
uses: pr-mpt/actions-commit-hash@v2
|
uses: pr-mpt/actions-commit-hash@v2
|
||||||
- name: Download task definition
|
- name: Download task definition
|
||||||
run: |
|
run: |
|
||||||
aws ecs describe-task-definition --task-definition infisical-core-platform --query taskDefinition > task-definition.json
|
aws ecs describe-task-definition --task-definition infisical-prod-platform --query taskDefinition > task-definition.json
|
||||||
- name: Render Amazon ECS task definition
|
- name: Render Amazon ECS task definition
|
||||||
id: render-web-container
|
id: render-web-container
|
||||||
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
||||||
with:
|
with:
|
||||||
task-definition: task-definition.json
|
task-definition: task-definition.json
|
||||||
container-name: infisical-core-platform
|
container-name: infisical-prod-platform
|
||||||
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||||
environment-variables: "LOG_LEVEL=info"
|
environment-variables: "LOG_LEVEL=info"
|
||||||
- name: Deploy to Amazon ECS service
|
- name: Deploy to Amazon ECS service
|
||||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
||||||
with:
|
with:
|
||||||
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
||||||
service: infisical-core-platform
|
service: infisical-prod-platform
|
||||||
cluster: infisical-core-platform
|
cluster: infisical-prod-platform
|
||||||
wait-for-service-stability: true
|
wait-for-service-stability: true
|
||||||
|
|
||||||
production-postgres-deployment:
|
production-postgres-deployment:
|
||||||
@@ -122,19 +122,19 @@ jobs:
|
|||||||
uses: pr-mpt/actions-commit-hash@v2
|
uses: pr-mpt/actions-commit-hash@v2
|
||||||
- name: Download task definition
|
- name: Download task definition
|
||||||
run: |
|
run: |
|
||||||
aws ecs describe-task-definition --task-definition infisical-core-platform --query taskDefinition > task-definition.json
|
aws ecs describe-task-definition --task-definition infisical-prod-platform --query taskDefinition > task-definition.json
|
||||||
- name: Render Amazon ECS task definition
|
- name: Render Amazon ECS task definition
|
||||||
id: render-web-container
|
id: render-web-container
|
||||||
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
||||||
with:
|
with:
|
||||||
task-definition: task-definition.json
|
task-definition: task-definition.json
|
||||||
container-name: infisical-core-platform
|
container-name: infisical-prod-platform
|
||||||
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||||
environment-variables: "LOG_LEVEL=info"
|
environment-variables: "LOG_LEVEL=info"
|
||||||
- name: Deploy to Amazon ECS service
|
- name: Deploy to Amazon ECS service
|
||||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
||||||
with:
|
with:
|
||||||
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
||||||
service: infisical-core-platform
|
service: infisical-prod-platform
|
||||||
cluster: infisical-core-platform
|
cluster: infisical-prod-platform
|
||||||
wait-for-service-stability: true
|
wait-for-service-stability: true
|
||||||
|
@@ -40,14 +40,13 @@ jobs:
|
|||||||
REDIS_URL: redis://172.17.0.1:6379
|
REDIS_URL: redis://172.17.0.1:6379
|
||||||
DB_CONNECTION_URI: postgres://infisical:infisical@172.17.0.1:5432/infisical?sslmode=disable
|
DB_CONNECTION_URI: postgres://infisical:infisical@172.17.0.1:5432/infisical?sslmode=disable
|
||||||
JWT_AUTH_SECRET: something-random
|
JWT_AUTH_SECRET: something-random
|
||||||
ENCRYPTION_KEY: 4bnfe4e407b8921c104518903515b218
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.21.5'
|
go-version: '1.21.5'
|
||||||
- name: Wait for container to be stable and check logs
|
- name: Wait for container to be stable and check logs
|
||||||
run: |
|
run: |
|
||||||
SECONDS=0
|
SECONDS=0
|
||||||
r HEALTHY=0
|
HEALTHY=0
|
||||||
while [ $SECONDS -lt 60 ]; do
|
while [ $SECONDS -lt 60 ]; do
|
||||||
if docker ps | grep infisical-api | grep -q healthy; then
|
if docker ps | grep infisical-api | grep -q healthy; then
|
||||||
echo "Container is healthy."
|
echo "Container is healthy."
|
||||||
@@ -74,4 +73,4 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
docker-compose -f "docker-compose.dev.yml" down
|
docker-compose -f "docker-compose.dev.yml" down
|
||||||
docker stop infisical-api
|
docker stop infisical-api
|
||||||
docker remove infisical-api
|
docker remove infisical-api
|
@@ -38,16 +38,6 @@ jobs:
|
|||||||
rm added_files.txt
|
rm added_files.txt
|
||||||
git commit -m "chore: renamed new migration files to latest timestamp (gh-action)"
|
git commit -m "chore: renamed new migration files to latest timestamp (gh-action)"
|
||||||
|
|
||||||
- name: Get PR details
|
|
||||||
id: pr_details
|
|
||||||
run: |
|
|
||||||
PR_NUMBER=${{ github.event.pull_request.number }}
|
|
||||||
PR_MERGER=$(curl -s "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" | jq -r '.merged_by.login')
|
|
||||||
|
|
||||||
echo "PR Number: $PR_NUMBER"
|
|
||||||
echo "PR Merger: $PR_MERGER"
|
|
||||||
echo "pr_merger=$PR_MERGER" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
if: env.SKIP_RENAME != 'true'
|
if: env.SKIP_RENAME != 'true'
|
||||||
uses: peter-evans/create-pull-request@v6
|
uses: peter-evans/create-pull-request@v6
|
||||||
@@ -56,4 +46,3 @@ jobs:
|
|||||||
commit-message: 'chore: renamed new migration files to latest UTC (gh-action)'
|
commit-message: 'chore: renamed new migration files to latest UTC (gh-action)'
|
||||||
title: 'GH Action: rename new migration file timestamp'
|
title: 'GH Action: rename new migration file timestamp'
|
||||||
branch-suffix: timestamp
|
branch-suffix: timestamp
|
||||||
reviewers: ${{ steps.pr_details.outputs.pr_merger }}
|
|
||||||
|
@@ -3,5 +3,4 @@ frontend/src/views/Project/MembersPage/components/IdentityTab/components/Identit
|
|||||||
frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:304
|
frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:304
|
||||||
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/MemberRbacSection.tsx:generic-api-key:206
|
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/MemberRbacSection.tsx:generic-api-key:206
|
||||||
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:292
|
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:292
|
||||||
docs/self-hosting/configuration/envars.mdx:generic-api-key:106
|
|
||||||
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:451
|
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:451
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
ARG POSTHOG_HOST=https://app.posthog.com
|
ARG POSTHOG_HOST=https://app.posthog.com
|
||||||
ARG POSTHOG_API_KEY=posthog-api-key
|
ARG POSTHOG_API_KEY=posthog-api-key
|
||||||
ARG INTERCOM_ID=intercom-id
|
ARG INTERCOM_ID=intercom-id
|
||||||
|
ARG SAML_ORG_SLUG=saml-org-slug-default
|
||||||
|
|
||||||
FROM node:20-alpine AS base
|
FROM node:20-alpine AS base
|
||||||
|
|
||||||
@@ -34,7 +35,9 @@ ENV NEXT_PUBLIC_POSTHOG_API_KEY $POSTHOG_API_KEY
|
|||||||
ARG INTERCOM_ID
|
ARG INTERCOM_ID
|
||||||
ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID
|
ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID
|
||||||
ARG INFISICAL_PLATFORM_VERSION
|
ARG INFISICAL_PLATFORM_VERSION
|
||||||
ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION
|
ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION
|
||||||
|
ARG SAML_ORG_SLUG
|
||||||
|
ENV NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
@@ -52,7 +55,6 @@ VOLUME /app/.next/cache/images
|
|||||||
COPY --chown=non-root-user:nodejs --chmod=555 frontend/scripts ./scripts
|
COPY --chown=non-root-user:nodejs --chmod=555 frontend/scripts ./scripts
|
||||||
COPY --from=frontend-builder /app/public ./public
|
COPY --from=frontend-builder /app/public ./public
|
||||||
RUN chown non-root-user:nodejs ./public/data
|
RUN chown non-root-user:nodejs ./public/data
|
||||||
|
|
||||||
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/standalone ./
|
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/standalone ./
|
||||||
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/static ./.next/static
|
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
@@ -91,18 +93,9 @@ RUN mkdir frontend-build
|
|||||||
|
|
||||||
# Production stage
|
# Production stage
|
||||||
FROM base AS production
|
FROM base AS production
|
||||||
RUN apk add --upgrade --no-cache ca-certificates
|
|
||||||
RUN addgroup --system --gid 1001 nodejs \
|
RUN addgroup --system --gid 1001 nodejs \
|
||||||
&& adduser --system --uid 1001 non-root-user
|
&& adduser --system --uid 1001 non-root-user
|
||||||
|
|
||||||
# Give non-root-user permission to update SSL certs
|
|
||||||
RUN chown -R non-root-user /etc/ssl/certs
|
|
||||||
RUN chown non-root-user /etc/ssl/certs/ca-certificates.crt
|
|
||||||
RUN chmod -R u+rwx /etc/ssl/certs
|
|
||||||
RUN chmod u+rw /etc/ssl/certs/ca-certificates.crt
|
|
||||||
RUN chown non-root-user /usr/sbin/update-ca-certificates
|
|
||||||
RUN chmod u+rx /usr/sbin/update-ca-certificates
|
|
||||||
|
|
||||||
## set pre baked keys
|
## set pre baked keys
|
||||||
ARG POSTHOG_API_KEY
|
ARG POSTHOG_API_KEY
|
||||||
ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
|
ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
|
||||||
@@ -110,6 +103,9 @@ ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
|
|||||||
ARG INTERCOM_ID=intercom-id
|
ARG INTERCOM_ID=intercom-id
|
||||||
ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
|
ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
|
||||||
BAKED_NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID
|
BAKED_NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID
|
||||||
|
ARG SAML_ORG_SLUG
|
||||||
|
ENV NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG \
|
||||||
|
BAKED_NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG
|
||||||
|
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
|
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { Lock } from "@app/lib/red-lock";
|
|
||||||
|
|
||||||
export const mockKeyStore = (): TKeyStoreFactory => {
|
export const mockKeyStore = (): TKeyStoreFactory => {
|
||||||
const store: Record<string, string | number | Buffer> = {};
|
const store: Record<string, string | number | Buffer> = {};
|
||||||
@@ -26,12 +25,6 @@ export const mockKeyStore = (): TKeyStoreFactory => {
|
|||||||
},
|
},
|
||||||
incrementBy: async () => {
|
incrementBy: async () => {
|
||||||
return 1;
|
return 1;
|
||||||
},
|
}
|
||||||
acquireLock: () => {
|
|
||||||
return Promise.resolve({
|
|
||||||
release: () => {}
|
|
||||||
}) as Promise<Lock>;
|
|
||||||
},
|
|
||||||
waitTillReady: async () => {}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
925
backend/package-lock.json
generated
925
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -95,13 +95,11 @@
|
|||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"axios-retry": "^4.0.0",
|
"axios-retry": "^4.0.0",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"bullmq": "^5.4.2",
|
"bullmq": "^5.3.3",
|
||||||
"cassandra-driver": "^4.7.2",
|
"cassandra-driver": "^4.7.2",
|
||||||
"dotenv": "^16.4.1",
|
"dotenv": "^16.4.1",
|
||||||
"fastify": "^4.26.0",
|
"fastify": "^4.26.0",
|
||||||
"fastify-plugin": "^4.5.1",
|
"fastify-plugin": "^4.5.1",
|
||||||
"google-auth-library": "^9.9.0",
|
|
||||||
"googleapis": "^137.1.0",
|
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
"ioredis": "^5.3.2",
|
"ioredis": "^5.3.2",
|
||||||
"jmespath": "^0.16.0",
|
"jmespath": "^0.16.0",
|
||||||
@@ -112,7 +110,7 @@
|
|||||||
"libsodium-wrappers": "^0.7.13",
|
"libsodium-wrappers": "^0.7.13",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
"ms": "^2.1.3",
|
"ms": "^2.1.3",
|
||||||
"mysql2": "^3.9.8",
|
"mysql2": "^3.9.4",
|
||||||
"nanoid": "^5.0.4",
|
"nanoid": "^5.0.4",
|
||||||
"nodemailer": "^6.9.9",
|
"nodemailer": "^6.9.9",
|
||||||
"ora": "^7.0.1",
|
"ora": "^7.0.1",
|
||||||
|
@@ -35,8 +35,6 @@ const getZodPrimitiveType = (type: string) => {
|
|||||||
return "z.coerce.number()";
|
return "z.coerce.number()";
|
||||||
case "text":
|
case "text":
|
||||||
return "z.string()";
|
return "z.string()";
|
||||||
case "bytea":
|
|
||||||
return "zodBuffer";
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Invalid type: ${type}`);
|
throw new Error(`Invalid type: ${type}`);
|
||||||
}
|
}
|
||||||
@@ -98,15 +96,10 @@ const main = async () => {
|
|||||||
const columnNames = Object.keys(columns);
|
const columnNames = Object.keys(columns);
|
||||||
|
|
||||||
let schema = "";
|
let schema = "";
|
||||||
const zodImportSet = new Set<string>();
|
|
||||||
for (let colNum = 0; colNum < columnNames.length; colNum++) {
|
for (let colNum = 0; colNum < columnNames.length; colNum++) {
|
||||||
const columnName = columnNames[colNum];
|
const columnName = columnNames[colNum];
|
||||||
const colInfo = columns[columnName];
|
const colInfo = columns[columnName];
|
||||||
let ztype = getZodPrimitiveType(colInfo.type);
|
let ztype = getZodPrimitiveType(colInfo.type);
|
||||||
if (["zodBuffer"].includes(ztype)) {
|
|
||||||
zodImportSet.add(ztype);
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't put optional on id
|
// don't put optional on id
|
||||||
if (colInfo.defaultValue && columnName !== "id") {
|
if (colInfo.defaultValue && columnName !== "id") {
|
||||||
const { defaultValue } = colInfo;
|
const { defaultValue } = colInfo;
|
||||||
@@ -128,8 +121,6 @@ const main = async () => {
|
|||||||
.split("_")
|
.split("_")
|
||||||
.reduce((prev, curr) => prev + `${curr.at(0)?.toUpperCase()}${curr.slice(1).toLowerCase()}`, "");
|
.reduce((prev, curr) => prev + `${curr.at(0)?.toUpperCase()}${curr.slice(1).toLowerCase()}`, "");
|
||||||
|
|
||||||
const zodImports = Array.from(zodImportSet);
|
|
||||||
|
|
||||||
// the insert and update are changed to zod input type to use default cases
|
// the insert and update are changed to zod input type to use default cases
|
||||||
writeFileSync(
|
writeFileSync(
|
||||||
path.join(__dirname, "../src/db/schemas", `${dashcase}.ts`),
|
path.join(__dirname, "../src/db/schemas", `${dashcase}.ts`),
|
||||||
@@ -140,8 +131,6 @@ const main = async () => {
|
|||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
${zodImports.length ? `import { ${zodImports.join(",")} } from \"@app/lib/zod\";` : ""}
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
export const ${pascalCase}Schema = z.object({${schema}});
|
export const ${pascalCase}Schema = z.object({${schema}});
|
||||||
|
12
backend/src/@types/fastify.d.ts
vendored
12
backend/src/@types/fastify.d.ts
vendored
@@ -32,10 +32,6 @@ import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-se
|
|||||||
import { TGroupProjectServiceFactory } from "@app/services/group-project/group-project-service";
|
import { TGroupProjectServiceFactory } from "@app/services/group-project/group-project-service";
|
||||||
import { TIdentityServiceFactory } from "@app/services/identity/identity-service";
|
import { TIdentityServiceFactory } from "@app/services/identity/identity-service";
|
||||||
import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
|
import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
|
||||||
import { TIdentityAwsAuthServiceFactory } from "@app/services/identity-aws-auth/identity-aws-auth-service";
|
|
||||||
import { TIdentityAzureAuthServiceFactory } from "@app/services/identity-azure-auth/identity-azure-auth-service";
|
|
||||||
import { TIdentityGcpAuthServiceFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-service";
|
|
||||||
import { TIdentityKubernetesAuthServiceFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-service";
|
|
||||||
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
||||||
import { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
|
import { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
|
||||||
import { TIntegrationServiceFactory } from "@app/services/integration/integration-service";
|
import { TIntegrationServiceFactory } from "@app/services/integration/integration-service";
|
||||||
@@ -52,8 +48,6 @@ import { TSecretServiceFactory } from "@app/services/secret/secret-service";
|
|||||||
import { TSecretBlindIndexServiceFactory } from "@app/services/secret-blind-index/secret-blind-index-service";
|
import { TSecretBlindIndexServiceFactory } from "@app/services/secret-blind-index/secret-blind-index-service";
|
||||||
import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
|
import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
|
||||||
import { TSecretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
|
import { TSecretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
|
||||||
import { TSecretReplicationServiceFactory } from "@app/services/secret-replication/secret-replication-service";
|
|
||||||
import { TSecretSharingServiceFactory } from "@app/services/secret-sharing/secret-sharing-service";
|
|
||||||
import { TSecretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service";
|
import { TSecretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service";
|
||||||
import { TServiceTokenServiceFactory } from "@app/services/service-token/service-token-service";
|
import { TServiceTokenServiceFactory } from "@app/services/service-token/service-token-service";
|
||||||
import { TSuperAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
|
import { TSuperAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
|
||||||
@@ -109,7 +103,6 @@ declare module "fastify" {
|
|||||||
projectKey: TProjectKeyServiceFactory;
|
projectKey: TProjectKeyServiceFactory;
|
||||||
projectRole: TProjectRoleServiceFactory;
|
projectRole: TProjectRoleServiceFactory;
|
||||||
secret: TSecretServiceFactory;
|
secret: TSecretServiceFactory;
|
||||||
secretReplication: TSecretReplicationServiceFactory;
|
|
||||||
secretTag: TSecretTagServiceFactory;
|
secretTag: TSecretTagServiceFactory;
|
||||||
secretImport: TSecretImportServiceFactory;
|
secretImport: TSecretImportServiceFactory;
|
||||||
projectBot: TProjectBotServiceFactory;
|
projectBot: TProjectBotServiceFactory;
|
||||||
@@ -122,10 +115,6 @@ declare module "fastify" {
|
|||||||
identityAccessToken: TIdentityAccessTokenServiceFactory;
|
identityAccessToken: TIdentityAccessTokenServiceFactory;
|
||||||
identityProject: TIdentityProjectServiceFactory;
|
identityProject: TIdentityProjectServiceFactory;
|
||||||
identityUa: TIdentityUaServiceFactory;
|
identityUa: TIdentityUaServiceFactory;
|
||||||
identityKubernetesAuth: TIdentityKubernetesAuthServiceFactory;
|
|
||||||
identityGcpAuth: TIdentityGcpAuthServiceFactory;
|
|
||||||
identityAwsAuth: TIdentityAwsAuthServiceFactory;
|
|
||||||
identityAzureAuth: TIdentityAzureAuthServiceFactory;
|
|
||||||
accessApprovalPolicy: TAccessApprovalPolicyServiceFactory;
|
accessApprovalPolicy: TAccessApprovalPolicyServiceFactory;
|
||||||
accessApprovalRequest: TAccessApprovalRequestServiceFactory;
|
accessApprovalRequest: TAccessApprovalRequestServiceFactory;
|
||||||
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
|
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
|
||||||
@@ -146,7 +135,6 @@ declare module "fastify" {
|
|||||||
dynamicSecretLease: TDynamicSecretLeaseServiceFactory;
|
dynamicSecretLease: TDynamicSecretLeaseServiceFactory;
|
||||||
projectUserAdditionalPrivilege: TProjectUserAdditionalPrivilegeServiceFactory;
|
projectUserAdditionalPrivilege: TProjectUserAdditionalPrivilegeServiceFactory;
|
||||||
identityProjectAdditionalPrivilege: TIdentityProjectAdditionalPrivilegeServiceFactory;
|
identityProjectAdditionalPrivilege: TIdentityProjectAdditionalPrivilegeServiceFactory;
|
||||||
secretSharing: TSecretSharingServiceFactory;
|
|
||||||
};
|
};
|
||||||
// this is exclusive use for middlewares in which we need to inject data
|
// this is exclusive use for middlewares in which we need to inject data
|
||||||
// everywhere else access using service layer
|
// everywhere else access using service layer
|
||||||
|
69
backend/src/@types/knex.d.ts
vendored
69
backend/src/@types/knex.d.ts
vendored
@@ -50,6 +50,9 @@ import {
|
|||||||
TGroupProjectMemberships,
|
TGroupProjectMemberships,
|
||||||
TGroupProjectMembershipsInsert,
|
TGroupProjectMembershipsInsert,
|
||||||
TGroupProjectMembershipsUpdate,
|
TGroupProjectMembershipsUpdate,
|
||||||
|
TGroupProjectUserAdditionalPrivilege,
|
||||||
|
TGroupProjectUserAdditionalPrivilegeInsert,
|
||||||
|
TGroupProjectUserAdditionalPrivilegeUpdate,
|
||||||
TGroups,
|
TGroups,
|
||||||
TGroupsInsert,
|
TGroupsInsert,
|
||||||
TGroupsUpdate,
|
TGroupsUpdate,
|
||||||
@@ -59,18 +62,6 @@ import {
|
|||||||
TIdentityAccessTokens,
|
TIdentityAccessTokens,
|
||||||
TIdentityAccessTokensInsert,
|
TIdentityAccessTokensInsert,
|
||||||
TIdentityAccessTokensUpdate,
|
TIdentityAccessTokensUpdate,
|
||||||
TIdentityAwsAuths,
|
|
||||||
TIdentityAwsAuthsInsert,
|
|
||||||
TIdentityAwsAuthsUpdate,
|
|
||||||
TIdentityAzureAuths,
|
|
||||||
TIdentityAzureAuthsInsert,
|
|
||||||
TIdentityAzureAuthsUpdate,
|
|
||||||
TIdentityGcpAuths,
|
|
||||||
TIdentityGcpAuthsInsert,
|
|
||||||
TIdentityGcpAuthsUpdate,
|
|
||||||
TIdentityKubernetesAuths,
|
|
||||||
TIdentityKubernetesAuthsInsert,
|
|
||||||
TIdentityKubernetesAuthsUpdate,
|
|
||||||
TIdentityOrgMemberships,
|
TIdentityOrgMemberships,
|
||||||
TIdentityOrgMembershipsInsert,
|
TIdentityOrgMembershipsInsert,
|
||||||
TIdentityOrgMembershipsUpdate,
|
TIdentityOrgMembershipsUpdate,
|
||||||
@@ -98,15 +89,6 @@ import {
|
|||||||
TIntegrations,
|
TIntegrations,
|
||||||
TIntegrationsInsert,
|
TIntegrationsInsert,
|
||||||
TIntegrationsUpdate,
|
TIntegrationsUpdate,
|
||||||
TKmsKeys,
|
|
||||||
TKmsKeysInsert,
|
|
||||||
TKmsKeysUpdate,
|
|
||||||
TKmsKeyVersions,
|
|
||||||
TKmsKeyVersionsInsert,
|
|
||||||
TKmsKeyVersionsUpdate,
|
|
||||||
TKmsRootConfig,
|
|
||||||
TKmsRootConfigInsert,
|
|
||||||
TKmsRootConfigUpdate,
|
|
||||||
TLdapConfigs,
|
TLdapConfigs,
|
||||||
TLdapConfigsInsert,
|
TLdapConfigsInsert,
|
||||||
TLdapConfigsUpdate,
|
TLdapConfigsUpdate,
|
||||||
@@ -185,9 +167,6 @@ import {
|
|||||||
TSecretImports,
|
TSecretImports,
|
||||||
TSecretImportsInsert,
|
TSecretImportsInsert,
|
||||||
TSecretImportsUpdate,
|
TSecretImportsUpdate,
|
||||||
TSecretReferences,
|
|
||||||
TSecretReferencesInsert,
|
|
||||||
TSecretReferencesUpdate,
|
|
||||||
TSecretRotationOutputs,
|
TSecretRotationOutputs,
|
||||||
TSecretRotationOutputsInsert,
|
TSecretRotationOutputsInsert,
|
||||||
TSecretRotationOutputsUpdate,
|
TSecretRotationOutputsUpdate,
|
||||||
@@ -198,9 +177,6 @@ import {
|
|||||||
TSecretScanningGitRisks,
|
TSecretScanningGitRisks,
|
||||||
TSecretScanningGitRisksInsert,
|
TSecretScanningGitRisksInsert,
|
||||||
TSecretScanningGitRisksUpdate,
|
TSecretScanningGitRisksUpdate,
|
||||||
TSecretSharing,
|
|
||||||
TSecretSharingInsert,
|
|
||||||
TSecretSharingUpdate,
|
|
||||||
TSecretsInsert,
|
TSecretsInsert,
|
||||||
TSecretSnapshotFolders,
|
TSecretSnapshotFolders,
|
||||||
TSecretSnapshotFoldersInsert,
|
TSecretSnapshotFoldersInsert,
|
||||||
@@ -317,6 +293,11 @@ declare module "knex/types/tables" {
|
|||||||
TProjectUserMembershipRolesInsert,
|
TProjectUserMembershipRolesInsert,
|
||||||
TProjectUserMembershipRolesUpdate
|
TProjectUserMembershipRolesUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.GroupProjectUserAdditionalPrivilege]: Knex.CompositeTableType<
|
||||||
|
TGroupProjectUserAdditionalPrivilege,
|
||||||
|
TGroupProjectUserAdditionalPrivilegeInsert,
|
||||||
|
TGroupProjectUserAdditionalPrivilegeUpdate
|
||||||
|
>;
|
||||||
[TableName.ProjectRoles]: Knex.CompositeTableType<TProjectRoles, TProjectRolesInsert, TProjectRolesUpdate>;
|
[TableName.ProjectRoles]: Knex.CompositeTableType<TProjectRoles, TProjectRolesInsert, TProjectRolesUpdate>;
|
||||||
[TableName.ProjectUserAdditionalPrivilege]: Knex.CompositeTableType<
|
[TableName.ProjectUserAdditionalPrivilege]: Knex.CompositeTableType<
|
||||||
TProjectUserAdditionalPrivilege,
|
TProjectUserAdditionalPrivilege,
|
||||||
@@ -325,11 +306,6 @@ declare module "knex/types/tables" {
|
|||||||
>;
|
>;
|
||||||
[TableName.ProjectKeys]: Knex.CompositeTableType<TProjectKeys, TProjectKeysInsert, TProjectKeysUpdate>;
|
[TableName.ProjectKeys]: Knex.CompositeTableType<TProjectKeys, TProjectKeysInsert, TProjectKeysUpdate>;
|
||||||
[TableName.Secret]: Knex.CompositeTableType<TSecrets, TSecretsInsert, TSecretsUpdate>;
|
[TableName.Secret]: Knex.CompositeTableType<TSecrets, TSecretsInsert, TSecretsUpdate>;
|
||||||
[TableName.SecretReference]: Knex.CompositeTableType<
|
|
||||||
TSecretReferences,
|
|
||||||
TSecretReferencesInsert,
|
|
||||||
TSecretReferencesUpdate
|
|
||||||
>;
|
|
||||||
[TableName.SecretBlindIndex]: Knex.CompositeTableType<
|
[TableName.SecretBlindIndex]: Knex.CompositeTableType<
|
||||||
TSecretBlindIndexes,
|
TSecretBlindIndexes,
|
||||||
TSecretBlindIndexesInsert,
|
TSecretBlindIndexesInsert,
|
||||||
@@ -342,7 +318,6 @@ declare module "knex/types/tables" {
|
|||||||
TSecretFolderVersionsInsert,
|
TSecretFolderVersionsInsert,
|
||||||
TSecretFolderVersionsUpdate
|
TSecretFolderVersionsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SecretSharing]: Knex.CompositeTableType<TSecretSharing, TSecretSharingInsert, TSecretSharingUpdate>;
|
|
||||||
[TableName.SecretTag]: Knex.CompositeTableType<TSecretTags, TSecretTagsInsert, TSecretTagsUpdate>;
|
[TableName.SecretTag]: Knex.CompositeTableType<TSecretTags, TSecretTagsInsert, TSecretTagsUpdate>;
|
||||||
[TableName.SecretImport]: Knex.CompositeTableType<TSecretImports, TSecretImportsInsert, TSecretImportsUpdate>;
|
[TableName.SecretImport]: Knex.CompositeTableType<TSecretImports, TSecretImportsInsert, TSecretImportsUpdate>;
|
||||||
[TableName.Integration]: Knex.CompositeTableType<TIntegrations, TIntegrationsInsert, TIntegrationsUpdate>;
|
[TableName.Integration]: Knex.CompositeTableType<TIntegrations, TIntegrationsInsert, TIntegrationsUpdate>;
|
||||||
@@ -359,26 +334,6 @@ declare module "knex/types/tables" {
|
|||||||
TIdentityUniversalAuthsInsert,
|
TIdentityUniversalAuthsInsert,
|
||||||
TIdentityUniversalAuthsUpdate
|
TIdentityUniversalAuthsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.IdentityKubernetesAuth]: Knex.CompositeTableType<
|
|
||||||
TIdentityKubernetesAuths,
|
|
||||||
TIdentityKubernetesAuthsInsert,
|
|
||||||
TIdentityKubernetesAuthsUpdate
|
|
||||||
>;
|
|
||||||
[TableName.IdentityGcpAuth]: Knex.CompositeTableType<
|
|
||||||
TIdentityGcpAuths,
|
|
||||||
TIdentityGcpAuthsInsert,
|
|
||||||
TIdentityGcpAuthsUpdate
|
|
||||||
>;
|
|
||||||
[TableName.IdentityAwsAuth]: Knex.CompositeTableType<
|
|
||||||
TIdentityAwsAuths,
|
|
||||||
TIdentityAwsAuthsInsert,
|
|
||||||
TIdentityAwsAuthsUpdate
|
|
||||||
>;
|
|
||||||
[TableName.IdentityAzureAuth]: Knex.CompositeTableType<
|
|
||||||
TIdentityAzureAuths,
|
|
||||||
TIdentityAzureAuthsInsert,
|
|
||||||
TIdentityAzureAuthsUpdate
|
|
||||||
>;
|
|
||||||
[TableName.IdentityUaClientSecret]: Knex.CompositeTableType<
|
[TableName.IdentityUaClientSecret]: Knex.CompositeTableType<
|
||||||
TIdentityUaClientSecrets,
|
TIdentityUaClientSecrets,
|
||||||
TIdentityUaClientSecretsInsert,
|
TIdentityUaClientSecretsInsert,
|
||||||
@@ -525,13 +480,5 @@ declare module "knex/types/tables" {
|
|||||||
TSecretVersionTagJunctionInsert,
|
TSecretVersionTagJunctionInsert,
|
||||||
TSecretVersionTagJunctionUpdate
|
TSecretVersionTagJunctionUpdate
|
||||||
>;
|
>;
|
||||||
// KMS service
|
|
||||||
[TableName.KmsServerRootConfig]: Knex.CompositeTableType<
|
|
||||||
TKmsRootConfig,
|
|
||||||
TKmsRootConfigInsert,
|
|
||||||
TKmsRootConfigUpdate
|
|
||||||
>;
|
|
||||||
[TableName.KmsKey]: Knex.CompositeTableType<TKmsKeys, TKmsKeysInsert, TKmsKeysUpdate>;
|
|
||||||
[TableName.KmsKeyVersion]: Knex.CompositeTableType<TKmsKeyVersions, TKmsKeyVersionsInsert, TKmsKeyVersionsUpdate>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,9 +9,8 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
t.string("name").notNullable();
|
t.string("name").notNullable();
|
||||||
t.integer("approvals").defaultTo(1).notNullable();
|
t.integer("approvals").defaultTo(1).notNullable();
|
||||||
t.string("secretPath");
|
|
||||||
|
|
||||||
t.uuid("envId").notNullable();
|
t.uuid("envId").notNullable();
|
||||||
|
t.string("secretPath");
|
||||||
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
|
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
|
||||||
t.timestamps(true, true, true);
|
t.timestamps(true, true, true);
|
||||||
});
|
});
|
||||||
@@ -21,8 +20,9 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
if (!(await knex.schema.hasTable(TableName.AccessApprovalPolicyApprover))) {
|
if (!(await knex.schema.hasTable(TableName.AccessApprovalPolicyApprover))) {
|
||||||
await knex.schema.createTable(TableName.AccessApprovalPolicyApprover, (t) => {
|
await knex.schema.createTable(TableName.AccessApprovalPolicyApprover, (t) => {
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
t.uuid("approverId").notNullable();
|
|
||||||
t.foreign("approverId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
t.uuid("approverUserId").nullable();
|
||||||
|
t.foreign("approverUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||||
|
|
||||||
t.uuid("policyId").notNullable();
|
t.uuid("policyId").notNullable();
|
||||||
t.foreign("policyId").references("id").inTable(TableName.AccessApprovalPolicy).onDelete("CASCADE");
|
t.foreign("policyId").references("id").inTable(TableName.AccessApprovalPolicy).onDelete("CASCADE");
|
@@ -0,0 +1,37 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasTable(TableName.GroupProjectUserAdditionalPrivilege))) {
|
||||||
|
await knex.schema.createTable(TableName.GroupProjectUserAdditionalPrivilege, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.string("slug", 60).notNullable();
|
||||||
|
|
||||||
|
t.uuid("groupProjectMembershipId").notNullable();
|
||||||
|
t.foreign("groupProjectMembershipId")
|
||||||
|
.references("id")
|
||||||
|
.inTable(TableName.GroupProjectMembership)
|
||||||
|
.onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.uuid("requestedByUserId").notNullable();
|
||||||
|
t.foreign("requestedByUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.boolean("isTemporary").notNullable().defaultTo(false);
|
||||||
|
t.string("temporaryMode");
|
||||||
|
t.string("temporaryRange"); // could be cron or relative time like 1H or 1minute etc
|
||||||
|
t.datetime("temporaryAccessStartTime");
|
||||||
|
t.datetime("temporaryAccessEndTime");
|
||||||
|
t.jsonb("permissions").notNullable();
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.GroupProjectUserAdditionalPrivilege);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.GroupProjectUserAdditionalPrivilege);
|
||||||
|
await knex.schema.dropTableIfExists(TableName.GroupProjectUserAdditionalPrivilege);
|
||||||
|
}
|
@@ -11,11 +11,26 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
t.uuid("policyId").notNullable();
|
t.uuid("policyId").notNullable();
|
||||||
t.foreign("policyId").references("id").inTable(TableName.AccessApprovalPolicy).onDelete("CASCADE");
|
t.foreign("policyId").references("id").inTable(TableName.AccessApprovalPolicy).onDelete("CASCADE");
|
||||||
|
|
||||||
t.uuid("privilegeId").nullable();
|
t.uuid("projectUserPrivilegeId").nullable();
|
||||||
t.foreign("privilegeId").references("id").inTable(TableName.ProjectUserAdditionalPrivilege).onDelete("CASCADE");
|
t.foreign("projectUserPrivilegeId")
|
||||||
|
.references("id")
|
||||||
|
.inTable(TableName.ProjectUserAdditionalPrivilege)
|
||||||
|
.onDelete("CASCADE");
|
||||||
|
|
||||||
t.uuid("requestedBy").notNullable();
|
t.uuid("groupProjectUserPrivilegeId").nullable();
|
||||||
t.foreign("requestedBy").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
t.foreign("groupProjectUserPrivilegeId")
|
||||||
|
.references("id")
|
||||||
|
.inTable(TableName.GroupProjectUserAdditionalPrivilege)
|
||||||
|
.onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.uuid("requestedByUserId").notNullable();
|
||||||
|
t.foreign("requestedByUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.uuid("projectMembershipId").nullable();
|
||||||
|
t.foreign("projectMembershipId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.uuid("groupMembershipId").nullable();
|
||||||
|
t.foreign("groupMembershipId").references("id").inTable(TableName.GroupProjectMembership).onDelete("CASCADE");
|
||||||
|
|
||||||
// We use these values to create the actual privilege at a later point in time.
|
// We use these values to create the actual privilege at a later point in time.
|
||||||
t.boolean("isTemporary").notNullable();
|
t.boolean("isTemporary").notNullable();
|
||||||
@@ -31,14 +46,17 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
if (!(await knex.schema.hasTable(TableName.AccessApprovalRequestReviewer))) {
|
if (!(await knex.schema.hasTable(TableName.AccessApprovalRequestReviewer))) {
|
||||||
await knex.schema.createTable(TableName.AccessApprovalRequestReviewer, (t) => {
|
await knex.schema.createTable(TableName.AccessApprovalRequestReviewer, (t) => {
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
t.uuid("member").notNullable();
|
|
||||||
t.foreign("member").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
t.uuid("memberUserId").notNullable();
|
||||||
|
t.foreign("memberUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||||
|
|
||||||
t.string("status").notNullable();
|
t.string("status").notNullable();
|
||||||
t.uuid("requestId").notNullable();
|
t.uuid("requestId").notNullable();
|
||||||
t.foreign("requestId").references("id").inTable(TableName.AccessApprovalRequest).onDelete("CASCADE");
|
t.foreign("requestId").references("id").inTable(TableName.AccessApprovalRequest).onDelete("CASCADE");
|
||||||
t.timestamps(true, true, true);
|
t.timestamps(true, true, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await createOnUpdateTrigger(knex, TableName.AccessApprovalRequestReviewer);
|
await createOnUpdateTrigger(knex, TableName.AccessApprovalRequestReviewer);
|
||||||
}
|
}
|
||||||
|
|
@@ -0,0 +1,71 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
// SecretApprovalPolicyApprover, approverUserId
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.SecretApprovalPolicyApprover, "approverUserId"))) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalPolicyApprover, (t) => {
|
||||||
|
t.uuid("approverId").nullable().alter();
|
||||||
|
|
||||||
|
t.uuid("approverUserId").nullable();
|
||||||
|
t.foreign("approverUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretApprovalRequest, statusChangeByUserId
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.SecretApprovalRequest, "statusChangeByUserId"))) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequest, (t) => {
|
||||||
|
t.uuid("statusChangeBy").nullable().alter();
|
||||||
|
|
||||||
|
t.uuid("statusChangeByUserId").nullable();
|
||||||
|
t.foreign("statusChangeByUserId").references("id").inTable(TableName.Users).onDelete("SET NULL");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretApprovalRequest, committerUserId
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.SecretApprovalRequest, "committerUserId"))) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequest, (t) => {
|
||||||
|
t.uuid("committerId").nullable().alter();
|
||||||
|
|
||||||
|
t.uuid("committerUserId").nullable();
|
||||||
|
t.foreign("committerUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretApprovalRequestReviewer, memberUserId
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.SecretApprovalRequestReviewer, "memberUserId"))) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequestReviewer, (t) => {
|
||||||
|
t.uuid("member").nullable().alter();
|
||||||
|
|
||||||
|
t.uuid("memberUserId").nullable();
|
||||||
|
t.foreign("memberUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.SecretApprovalPolicyApprover, "approverUserId")) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalPolicyApprover, (t) => {
|
||||||
|
t.dropColumn("approverUserId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.SecretApprovalRequest, "statusChangeByUserId")) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequest, (t) => {
|
||||||
|
t.dropColumn("statusChangeByUserId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.SecretApprovalRequest, "committerUserId")) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequest, (t) => {
|
||||||
|
t.dropColumn("committerUserId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.SecretApprovalRequestReviewer, "memberUserId")) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalRequestReviewer, (t) => {
|
||||||
|
t.dropColumn("memberUserId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -1,54 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
const isUsersTablePresent = await knex.schema.hasTable(TableName.Users);
|
|
||||||
if (isUsersTablePresent) {
|
|
||||||
const hasIsEmailVerifiedColumn = await knex.schema.hasColumn(TableName.Users, "isEmailVerified");
|
|
||||||
|
|
||||||
if (!hasIsEmailVerifiedColumn) {
|
|
||||||
await knex.schema.alterTable(TableName.Users, (t) => {
|
|
||||||
t.boolean("isEmailVerified").defaultTo(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backfilling the isEmailVerified to true where isAccepted is true
|
|
||||||
await knex(TableName.Users).update({ isEmailVerified: true }).where("isAccepted", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const isUserAliasTablePresent = await knex.schema.hasTable(TableName.UserAliases);
|
|
||||||
if (isUserAliasTablePresent) {
|
|
||||||
await knex.schema.alterTable(TableName.UserAliases, (t) => {
|
|
||||||
t.string("username").nullable().alter();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const isSuperAdminTablePresent = await knex.schema.hasTable(TableName.SuperAdmin);
|
|
||||||
if (isSuperAdminTablePresent) {
|
|
||||||
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
|
||||||
t.boolean("trustSamlEmails").defaultTo(false);
|
|
||||||
t.boolean("trustLdapEmails").defaultTo(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
if (await knex.schema.hasColumn(TableName.Users, "isEmailVerified")) {
|
|
||||||
await knex.schema.alterTable(TableName.Users, (t) => {
|
|
||||||
t.dropColumn("isEmailVerified");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await knex.schema.hasColumn(TableName.SuperAdmin, "trustSamlEmails")) {
|
|
||||||
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
|
||||||
t.dropColumn("trustSamlEmails");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await knex.schema.hasColumn(TableName.SuperAdmin, "trustLdapEmails")) {
|
|
||||||
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
|
||||||
t.dropColumn("trustLdapEmails");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,30 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
if (!(await knex.schema.hasTable(TableName.IdentityAwsAuth))) {
|
|
||||||
await knex.schema.createTable(TableName.IdentityAwsAuth, (t) => {
|
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
|
||||||
t.bigInteger("accessTokenTTL").defaultTo(7200).notNullable();
|
|
||||||
t.bigInteger("accessTokenMaxTTL").defaultTo(7200).notNullable();
|
|
||||||
t.bigInteger("accessTokenNumUsesLimit").defaultTo(0).notNullable();
|
|
||||||
t.jsonb("accessTokenTrustedIps").notNullable();
|
|
||||||
t.timestamps(true, true, true);
|
|
||||||
t.uuid("identityId").notNullable().unique();
|
|
||||||
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
|
|
||||||
t.string("type").notNullable();
|
|
||||||
t.string("stsEndpoint").notNullable();
|
|
||||||
t.string("allowedPrincipalArns").notNullable();
|
|
||||||
t.string("allowedAccountIds").notNullable();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await createOnUpdateTrigger(knex, TableName.IdentityAwsAuth);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.dropTableIfExists(TableName.IdentityAwsAuth);
|
|
||||||
await dropOnUpdateTrigger(knex, TableName.IdentityAwsAuth);
|
|
||||||
}
|
|
@@ -1,30 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
if (!(await knex.schema.hasTable(TableName.IdentityGcpAuth))) {
|
|
||||||
await knex.schema.createTable(TableName.IdentityGcpAuth, (t) => {
|
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
|
||||||
t.bigInteger("accessTokenTTL").defaultTo(7200).notNullable();
|
|
||||||
t.bigInteger("accessTokenMaxTTL").defaultTo(7200).notNullable();
|
|
||||||
t.bigInteger("accessTokenNumUsesLimit").defaultTo(0).notNullable();
|
|
||||||
t.jsonb("accessTokenTrustedIps").notNullable();
|
|
||||||
t.timestamps(true, true, true);
|
|
||||||
t.uuid("identityId").notNullable().unique();
|
|
||||||
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
|
|
||||||
t.string("type").notNullable();
|
|
||||||
t.string("allowedServiceAccounts").notNullable();
|
|
||||||
t.string("allowedProjects").notNullable();
|
|
||||||
t.string("allowedZones").notNullable(); // GCE only (fully qualified zone names)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await createOnUpdateTrigger(knex, TableName.IdentityGcpAuth);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.dropTableIfExists(TableName.IdentityGcpAuth);
|
|
||||||
await dropOnUpdateTrigger(knex, TableName.IdentityGcpAuth);
|
|
||||||
}
|
|
@@ -1,24 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
if (!(await knex.schema.hasTable(TableName.SecretReference))) {
|
|
||||||
await knex.schema.createTable(TableName.SecretReference, (t) => {
|
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
|
||||||
t.string("environment").notNullable();
|
|
||||||
t.string("secretPath").notNullable();
|
|
||||||
t.uuid("secretId").notNullable();
|
|
||||||
t.foreign("secretId").references("id").inTable(TableName.Secret).onDelete("CASCADE");
|
|
||||||
t.timestamps(true, true, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
await createOnUpdateTrigger(knex, TableName.SecretReference);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.dropTableIfExists(TableName.SecretReference);
|
|
||||||
await dropOnUpdateTrigger(knex, TableName.SecretReference);
|
|
||||||
}
|
|
@@ -1,36 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
if (!(await knex.schema.hasTable(TableName.IdentityKubernetesAuth))) {
|
|
||||||
await knex.schema.createTable(TableName.IdentityKubernetesAuth, (t) => {
|
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
|
||||||
t.bigInteger("accessTokenTTL").defaultTo(7200).notNullable();
|
|
||||||
t.bigInteger("accessTokenMaxTTL").defaultTo(7200).notNullable();
|
|
||||||
t.bigInteger("accessTokenNumUsesLimit").defaultTo(0).notNullable();
|
|
||||||
t.jsonb("accessTokenTrustedIps").notNullable();
|
|
||||||
t.timestamps(true, true, true);
|
|
||||||
t.uuid("identityId").notNullable().unique();
|
|
||||||
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
|
|
||||||
t.string("kubernetesHost").notNullable();
|
|
||||||
t.text("encryptedCaCert").notNullable();
|
|
||||||
t.string("caCertIV").notNullable();
|
|
||||||
t.string("caCertTag").notNullable();
|
|
||||||
t.text("encryptedTokenReviewerJwt").notNullable();
|
|
||||||
t.string("tokenReviewerJwtIV").notNullable();
|
|
||||||
t.string("tokenReviewerJwtTag").notNullable();
|
|
||||||
t.string("allowedNamespaces").notNullable();
|
|
||||||
t.string("allowedNames").notNullable();
|
|
||||||
t.string("allowedAudience").notNullable();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await createOnUpdateTrigger(knex, TableName.IdentityKubernetesAuth);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.dropTableIfExists(TableName.IdentityKubernetesAuth);
|
|
||||||
await dropOnUpdateTrigger(knex, TableName.IdentityKubernetesAuth);
|
|
||||||
}
|
|
@@ -1,43 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
const hasIsSyncedColumn = await knex.schema.hasColumn(TableName.Integration, "isSynced");
|
|
||||||
const hasSyncMessageColumn = await knex.schema.hasColumn(TableName.Integration, "syncMessage");
|
|
||||||
const hasLastSyncJobId = await knex.schema.hasColumn(TableName.Integration, "lastSyncJobId");
|
|
||||||
|
|
||||||
await knex.schema.alterTable(TableName.Integration, (t) => {
|
|
||||||
if (!hasIsSyncedColumn) {
|
|
||||||
t.boolean("isSynced").nullable();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasSyncMessageColumn) {
|
|
||||||
t.text("syncMessage").nullable();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasLastSyncJobId) {
|
|
||||||
t.string("lastSyncJobId").nullable();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
const hasIsSyncedColumn = await knex.schema.hasColumn(TableName.Integration, "isSynced");
|
|
||||||
const hasSyncMessageColumn = await knex.schema.hasColumn(TableName.Integration, "syncMessage");
|
|
||||||
const hasLastSyncJobId = await knex.schema.hasColumn(TableName.Integration, "lastSyncJobId");
|
|
||||||
|
|
||||||
await knex.schema.alterTable(TableName.Integration, (t) => {
|
|
||||||
if (hasIsSyncedColumn) {
|
|
||||||
t.dropColumn("isSynced");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasSyncMessageColumn) {
|
|
||||||
t.dropColumn("syncMessage");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasLastSyncJobId) {
|
|
||||||
t.dropColumn("lastSyncJobId");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
@@ -1,26 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
const doesOrgIdExist = await knex.schema.hasColumn(TableName.AuditLog, "orgId");
|
|
||||||
const doesProjectIdExist = await knex.schema.hasColumn(TableName.AuditLog, "projectId");
|
|
||||||
if (await knex.schema.hasTable(TableName.AuditLog)) {
|
|
||||||
await knex.schema.alterTable(TableName.AuditLog, (t) => {
|
|
||||||
if (doesProjectIdExist) t.index("projectId");
|
|
||||||
if (doesOrgIdExist) t.index("orgId");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
const doesOrgIdExist = await knex.schema.hasColumn(TableName.AuditLog, "orgId");
|
|
||||||
const doesProjectIdExist = await knex.schema.hasColumn(TableName.AuditLog, "projectId");
|
|
||||||
|
|
||||||
if (await knex.schema.hasTable(TableName.AuditLog)) {
|
|
||||||
await knex.schema.alterTable(TableName.AuditLog, (t) => {
|
|
||||||
if (doesProjectIdExist) t.dropIndex("projectId");
|
|
||||||
if (doesOrgIdExist) t.dropIndex("orgId");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,22 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
const doesEnvIdExist = await knex.schema.hasColumn(TableName.SnapshotSecret, "envId");
|
|
||||||
if (await knex.schema.hasTable(TableName.SnapshotSecret)) {
|
|
||||||
await knex.schema.alterTable(TableName.SnapshotSecret, (t) => {
|
|
||||||
if (doesEnvIdExist) t.index("envId");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
const doesEnvIdExist = await knex.schema.hasColumn(TableName.SnapshotSecret, "envId");
|
|
||||||
|
|
||||||
if (await knex.schema.hasTable(TableName.SnapshotSecret)) {
|
|
||||||
await knex.schema.alterTable(TableName.SnapshotSecret, (t) => {
|
|
||||||
if (doesEnvIdExist) t.dropIndex("envId");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,22 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
const doesEnvIdExist = await knex.schema.hasColumn(TableName.SecretVersion, "envId");
|
|
||||||
if (await knex.schema.hasTable(TableName.SecretVersion)) {
|
|
||||||
await knex.schema.alterTable(TableName.SecretVersion, (t) => {
|
|
||||||
if (doesEnvIdExist) t.index("envId");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
const doesEnvIdExist = await knex.schema.hasColumn(TableName.SecretVersion, "envId");
|
|
||||||
|
|
||||||
if (await knex.schema.hasTable(TableName.SecretVersion)) {
|
|
||||||
await knex.schema.alterTable(TableName.SecretVersion, (t) => {
|
|
||||||
if (doesEnvIdExist) t.dropIndex("envId");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
const doesSnapshotIdExist = await knex.schema.hasColumn(TableName.SnapshotSecret, "snapshotId");
|
|
||||||
if (await knex.schema.hasTable(TableName.SnapshotSecret)) {
|
|
||||||
await knex.schema.alterTable(TableName.SnapshotSecret, (t) => {
|
|
||||||
if (doesSnapshotIdExist) t.index("snapshotId");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
const doesSnapshotIdExist = await knex.schema.hasColumn(TableName.SnapshotSecret, "snapshotId");
|
|
||||||
if (await knex.schema.hasTable(TableName.SnapshotSecret)) {
|
|
||||||
await knex.schema.alterTable(TableName.SnapshotSecret, (t) => {
|
|
||||||
if (doesSnapshotIdExist) t.dropIndex("snapshotId");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
const doesSnapshotIdExist = await knex.schema.hasColumn(TableName.SnapshotFolder, "snapshotId");
|
|
||||||
if (await knex.schema.hasTable(TableName.SnapshotFolder)) {
|
|
||||||
await knex.schema.alterTable(TableName.SnapshotFolder, (t) => {
|
|
||||||
if (doesSnapshotIdExist) t.index("snapshotId");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
const doesSnapshotIdExist = await knex.schema.hasColumn(TableName.SnapshotFolder, "snapshotId");
|
|
||||||
if (await knex.schema.hasTable(TableName.SnapshotFolder)) {
|
|
||||||
await knex.schema.alterTable(TableName.SnapshotFolder, (t) => {
|
|
||||||
if (doesSnapshotIdExist) t.dropIndex("snapshotId");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,24 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
const doesFolderIdExist = await knex.schema.hasColumn(TableName.Secret, "folderId");
|
|
||||||
const doesUserIdExist = await knex.schema.hasColumn(TableName.Secret, "userId");
|
|
||||||
if (await knex.schema.hasTable(TableName.Secret)) {
|
|
||||||
await knex.schema.alterTable(TableName.Secret, (t) => {
|
|
||||||
if (doesFolderIdExist && doesUserIdExist) t.index(["folderId", "userId"]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
const doesFolderIdExist = await knex.schema.hasColumn(TableName.Secret, "folderId");
|
|
||||||
const doesUserIdExist = await knex.schema.hasColumn(TableName.Secret, "userId");
|
|
||||||
|
|
||||||
if (await knex.schema.hasTable(TableName.Secret)) {
|
|
||||||
await knex.schema.alterTable(TableName.Secret, (t) => {
|
|
||||||
if (doesUserIdExist && doesFolderIdExist) t.dropIndex(["folderId", "userId"]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,22 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
const doesExpireAtExist = await knex.schema.hasColumn(TableName.AuditLog, "expiresAt");
|
|
||||||
if (await knex.schema.hasTable(TableName.AuditLog)) {
|
|
||||||
await knex.schema.alterTable(TableName.AuditLog, (t) => {
|
|
||||||
if (doesExpireAtExist) t.index("expiresAt");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
const doesExpireAtExist = await knex.schema.hasColumn(TableName.AuditLog, "expiresAt");
|
|
||||||
|
|
||||||
if (await knex.schema.hasTable(TableName.AuditLog)) {
|
|
||||||
await knex.schema.alterTable(TableName.AuditLog, (t) => {
|
|
||||||
if (doesExpireAtExist) t.dropIndex("expiresAt");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,29 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
if (!(await knex.schema.hasTable(TableName.IdentityAzureAuth))) {
|
|
||||||
await knex.schema.createTable(TableName.IdentityAzureAuth, (t) => {
|
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
|
||||||
t.bigInteger("accessTokenTTL").defaultTo(7200).notNullable();
|
|
||||||
t.bigInteger("accessTokenMaxTTL").defaultTo(7200).notNullable();
|
|
||||||
t.bigInteger("accessTokenNumUsesLimit").defaultTo(0).notNullable();
|
|
||||||
t.jsonb("accessTokenTrustedIps").notNullable();
|
|
||||||
t.timestamps(true, true, true);
|
|
||||||
t.uuid("identityId").notNullable().unique();
|
|
||||||
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
|
|
||||||
t.string("tenantId").notNullable();
|
|
||||||
t.string("resource").notNullable();
|
|
||||||
t.string("allowedServicePrincipalIds").notNullable();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await createOnUpdateTrigger(knex, TableName.IdentityAzureAuth);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.dropTableIfExists(TableName.IdentityAzureAuth);
|
|
||||||
await dropOnUpdateTrigger(knex, TableName.IdentityAzureAuth);
|
|
||||||
}
|
|
@@ -1,43 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
const hasConsecutiveFailedMfaAttempts = await knex.schema.hasColumn(TableName.Users, "consecutiveFailedMfaAttempts");
|
|
||||||
const hasIsLocked = await knex.schema.hasColumn(TableName.Users, "isLocked");
|
|
||||||
const hasTemporaryLockDateEnd = await knex.schema.hasColumn(TableName.Users, "temporaryLockDateEnd");
|
|
||||||
|
|
||||||
await knex.schema.alterTable(TableName.Users, (t) => {
|
|
||||||
if (!hasConsecutiveFailedMfaAttempts) {
|
|
||||||
t.integer("consecutiveFailedMfaAttempts").defaultTo(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasIsLocked) {
|
|
||||||
t.boolean("isLocked").defaultTo(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasTemporaryLockDateEnd) {
|
|
||||||
t.dateTime("temporaryLockDateEnd").nullable();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
const hasConsecutiveFailedMfaAttempts = await knex.schema.hasColumn(TableName.Users, "consecutiveFailedMfaAttempts");
|
|
||||||
const hasIsLocked = await knex.schema.hasColumn(TableName.Users, "isLocked");
|
|
||||||
const hasTemporaryLockDateEnd = await knex.schema.hasColumn(TableName.Users, "temporaryLockDateEnd");
|
|
||||||
|
|
||||||
await knex.schema.alterTable(TableName.Users, (t) => {
|
|
||||||
if (hasConsecutiveFailedMfaAttempts) {
|
|
||||||
t.dropColumn("consecutiveFailedMfaAttempts");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasIsLocked) {
|
|
||||||
t.dropColumn("isLocked");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasTemporaryLockDateEnd) {
|
|
||||||
t.dropColumn("temporaryLockDateEnd");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
@@ -1,29 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
import { createOnUpdateTrigger } from "../utils";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
if (!(await knex.schema.hasTable(TableName.SecretSharing))) {
|
|
||||||
await knex.schema.createTable(TableName.SecretSharing, (t) => {
|
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
|
||||||
t.string("name").notNullable();
|
|
||||||
t.text("encryptedValue").notNullable();
|
|
||||||
t.text("iv").notNullable();
|
|
||||||
t.text("tag").notNullable();
|
|
||||||
t.text("hashedHex").notNullable();
|
|
||||||
t.timestamp("expiresAt").notNullable();
|
|
||||||
t.uuid("userId").notNullable();
|
|
||||||
t.uuid("orgId").notNullable();
|
|
||||||
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
|
||||||
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
|
||||||
t.timestamps(true, true, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
await createOnUpdateTrigger(knex, TableName.SecretSharing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.dropTableIfExists(TableName.SecretSharing);
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
const doesSecretVersionIdExist = await knex.schema.hasColumn(TableName.SnapshotSecret, "secretVersionId");
|
|
||||||
if (await knex.schema.hasTable(TableName.SnapshotSecret)) {
|
|
||||||
await knex.schema.alterTable(TableName.SnapshotSecret, (t) => {
|
|
||||||
if (doesSecretVersionIdExist) t.index("secretVersionId");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
const doesSecretVersionIdExist = await knex.schema.hasColumn(TableName.SnapshotSecret, "secretVersionId");
|
|
||||||
if (await knex.schema.hasTable(TableName.SnapshotSecret)) {
|
|
||||||
await knex.schema.alterTable(TableName.SnapshotSecret, (t) => {
|
|
||||||
if (doesSecretVersionIdExist) t.dropIndex("secretVersionId");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,29 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
import { createOnUpdateTrigger } from "../utils";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
if (!(await knex.schema.hasTable(TableName.SecretSharing))) {
|
|
||||||
await knex.schema.createTable(TableName.SecretSharing, (t) => {
|
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
|
||||||
t.string("name").notNullable();
|
|
||||||
t.text("encryptedValue").notNullable();
|
|
||||||
t.text("iv").notNullable();
|
|
||||||
t.text("tag").notNullable();
|
|
||||||
t.text("hashedHex").notNullable();
|
|
||||||
t.timestamp("expiresAt").notNullable();
|
|
||||||
t.uuid("userId").notNullable();
|
|
||||||
t.uuid("orgId").notNullable();
|
|
||||||
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
|
||||||
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
|
||||||
t.timestamps(true, true, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
await createOnUpdateTrigger(knex, TableName.SecretSharing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.dropTableIfExists(TableName.SecretSharing);
|
|
||||||
}
|
|
@@ -1,33 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
const hasExpiresAfterViewsColumn = await knex.schema.hasColumn(TableName.SecretSharing, "expiresAfterViews");
|
|
||||||
const hasSecretNameColumn = await knex.schema.hasColumn(TableName.SecretSharing, "name");
|
|
||||||
|
|
||||||
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
|
|
||||||
if (!hasExpiresAfterViewsColumn) {
|
|
||||||
t.integer("expiresAfterViews");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasSecretNameColumn) {
|
|
||||||
t.dropColumn("name");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
const hasExpiresAfterViewsColumn = await knex.schema.hasColumn(TableName.SecretSharing, "expiresAfterViews");
|
|
||||||
const hasSecretNameColumn = await knex.schema.hasColumn(TableName.SecretSharing, "name");
|
|
||||||
|
|
||||||
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
|
|
||||||
if (hasExpiresAfterViewsColumn) {
|
|
||||||
t.dropColumn("expiresAfterViews");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasSecretNameColumn) {
|
|
||||||
t.string("name").notNullable();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
@@ -1,85 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
const doesSecretImportIsReplicationExist = await knex.schema.hasColumn(TableName.SecretImport, "isReplication");
|
|
||||||
const doesSecretImportIsReplicationSuccessExist = await knex.schema.hasColumn(
|
|
||||||
TableName.SecretImport,
|
|
||||||
"isReplicationSuccess"
|
|
||||||
);
|
|
||||||
const doesSecretImportReplicationStatusExist = await knex.schema.hasColumn(
|
|
||||||
TableName.SecretImport,
|
|
||||||
"replicationStatus"
|
|
||||||
);
|
|
||||||
const doesSecretImportLastReplicatedExist = await knex.schema.hasColumn(TableName.SecretImport, "lastReplicated");
|
|
||||||
const doesSecretImportIsReservedExist = await knex.schema.hasColumn(TableName.SecretImport, "isReserved");
|
|
||||||
|
|
||||||
if (await knex.schema.hasTable(TableName.SecretImport)) {
|
|
||||||
await knex.schema.alterTable(TableName.SecretImport, (t) => {
|
|
||||||
if (!doesSecretImportIsReplicationExist) t.boolean("isReplication").defaultTo(false);
|
|
||||||
if (!doesSecretImportIsReplicationSuccessExist) t.boolean("isReplicationSuccess").nullable();
|
|
||||||
if (!doesSecretImportReplicationStatusExist) t.text("replicationStatus").nullable();
|
|
||||||
if (!doesSecretImportLastReplicatedExist) t.datetime("lastReplicated").nullable();
|
|
||||||
if (!doesSecretImportIsReservedExist) t.boolean("isReserved").defaultTo(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const doesSecretFolderReservedExist = await knex.schema.hasColumn(TableName.SecretFolder, "isReserved");
|
|
||||||
if (await knex.schema.hasTable(TableName.SecretFolder)) {
|
|
||||||
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
|
|
||||||
if (!doesSecretFolderReservedExist) t.boolean("isReserved").defaultTo(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const doesSecretApprovalRequestIsReplicatedExist = await knex.schema.hasColumn(
|
|
||||||
TableName.SecretApprovalRequest,
|
|
||||||
"isReplicated"
|
|
||||||
);
|
|
||||||
if (await knex.schema.hasTable(TableName.SecretApprovalRequest)) {
|
|
||||||
await knex.schema.alterTable(TableName.SecretApprovalRequest, (t) => {
|
|
||||||
if (!doesSecretApprovalRequestIsReplicatedExist) t.boolean("isReplicated");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
const doesSecretImportIsReplicationExist = await knex.schema.hasColumn(TableName.SecretImport, "isReplication");
|
|
||||||
const doesSecretImportIsReplicationSuccessExist = await knex.schema.hasColumn(
|
|
||||||
TableName.SecretImport,
|
|
||||||
"isReplicationSuccess"
|
|
||||||
);
|
|
||||||
const doesSecretImportReplicationStatusExist = await knex.schema.hasColumn(
|
|
||||||
TableName.SecretImport,
|
|
||||||
"replicationStatus"
|
|
||||||
);
|
|
||||||
const doesSecretImportLastReplicatedExist = await knex.schema.hasColumn(TableName.SecretImport, "lastReplicated");
|
|
||||||
const doesSecretImportIsReservedExist = await knex.schema.hasColumn(TableName.SecretImport, "isReserved");
|
|
||||||
|
|
||||||
if (await knex.schema.hasTable(TableName.SecretImport)) {
|
|
||||||
await knex.schema.alterTable(TableName.SecretImport, (t) => {
|
|
||||||
if (doesSecretImportIsReplicationExist) t.dropColumn("isReplication");
|
|
||||||
if (doesSecretImportIsReplicationSuccessExist) t.dropColumn("isReplicationSuccess");
|
|
||||||
if (doesSecretImportReplicationStatusExist) t.dropColumn("replicationStatus");
|
|
||||||
if (doesSecretImportLastReplicatedExist) t.dropColumn("lastReplicated");
|
|
||||||
if (doesSecretImportIsReservedExist) t.dropColumn("isReserved");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const doesSecretFolderReservedExist = await knex.schema.hasColumn(TableName.SecretFolder, "isReserved");
|
|
||||||
if (await knex.schema.hasTable(TableName.SecretFolder)) {
|
|
||||||
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
|
|
||||||
if (doesSecretFolderReservedExist) t.dropColumn("isReserved");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const doesSecretApprovalRequestIsReplicatedExist = await knex.schema.hasColumn(
|
|
||||||
TableName.SecretApprovalRequest,
|
|
||||||
"isReplicated"
|
|
||||||
);
|
|
||||||
if (await knex.schema.hasTable(TableName.SecretApprovalRequest)) {
|
|
||||||
await knex.schema.alterTable(TableName.SecretApprovalRequest, (t) => {
|
|
||||||
if (doesSecretApprovalRequestIsReplicatedExist) t.dropColumn("isReplicated");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,56 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TableName } from "../schemas";
|
|
||||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
|
||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
|
||||||
if (!(await knex.schema.hasTable(TableName.KmsServerRootConfig))) {
|
|
||||||
await knex.schema.createTable(TableName.KmsServerRootConfig, (t) => {
|
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
|
||||||
t.binary("encryptedRootKey").notNullable();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await createOnUpdateTrigger(knex, TableName.KmsServerRootConfig);
|
|
||||||
|
|
||||||
if (!(await knex.schema.hasTable(TableName.KmsKey))) {
|
|
||||||
await knex.schema.createTable(TableName.KmsKey, (t) => {
|
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
|
||||||
t.binary("encryptedKey").notNullable();
|
|
||||||
t.string("encryptionAlgorithm").notNullable();
|
|
||||||
t.integer("version").defaultTo(1).notNullable();
|
|
||||||
t.string("description");
|
|
||||||
t.boolean("isDisabled").defaultTo(false);
|
|
||||||
t.boolean("isReserved").defaultTo(true);
|
|
||||||
t.string("projectId");
|
|
||||||
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
|
||||||
t.uuid("orgId");
|
|
||||||
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await createOnUpdateTrigger(knex, TableName.KmsKey);
|
|
||||||
|
|
||||||
if (!(await knex.schema.hasTable(TableName.KmsKeyVersion))) {
|
|
||||||
await knex.schema.createTable(TableName.KmsKeyVersion, (t) => {
|
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
|
||||||
t.binary("encryptedKey").notNullable();
|
|
||||||
t.integer("version").notNullable();
|
|
||||||
t.uuid("kmsKeyId").notNullable();
|
|
||||||
t.foreign("kmsKeyId").references("id").inTable(TableName.KmsKey).onDelete("CASCADE");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await createOnUpdateTrigger(knex, TableName.KmsKeyVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.dropTableIfExists(TableName.KmsServerRootConfig);
|
|
||||||
await dropOnUpdateTrigger(knex, TableName.KmsServerRootConfig);
|
|
||||||
|
|
||||||
await knex.schema.dropTableIfExists(TableName.KmsKeyVersion);
|
|
||||||
await dropOnUpdateTrigger(knex, TableName.KmsKeyVersion);
|
|
||||||
|
|
||||||
await knex.schema.dropTableIfExists(TableName.KmsKey);
|
|
||||||
await dropOnUpdateTrigger(knex, TableName.KmsKey);
|
|
||||||
}
|
|
@@ -9,7 +9,7 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
|
|
||||||
export const AccessApprovalPoliciesApproversSchema = z.object({
|
export const AccessApprovalPoliciesApproversSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
approverId: z.string().uuid(),
|
approverUserId: z.string().uuid().nullable().optional(),
|
||||||
policyId: z.string().uuid(),
|
policyId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date()
|
||||||
|
@@ -11,8 +11,8 @@ export const AccessApprovalPoliciesSchema = z.object({
|
|||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
approvals: z.number().default(1),
|
approvals: z.number().default(1),
|
||||||
secretPath: z.string().nullable().optional(),
|
|
||||||
envId: z.string().uuid(),
|
envId: z.string().uuid(),
|
||||||
|
secretPath: z.string().nullable().optional(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date()
|
||||||
});
|
});
|
||||||
|
@@ -9,7 +9,7 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
|
|
||||||
export const AccessApprovalRequestsReviewersSchema = z.object({
|
export const AccessApprovalRequestsReviewersSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
member: z.string().uuid(),
|
memberUserId: z.string().uuid(),
|
||||||
status: z.string(),
|
status: z.string(),
|
||||||
requestId: z.string().uuid(),
|
requestId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
|
@@ -10,8 +10,11 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
export const AccessApprovalRequestsSchema = z.object({
|
export const AccessApprovalRequestsSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
policyId: z.string().uuid(),
|
policyId: z.string().uuid(),
|
||||||
privilegeId: z.string().uuid().nullable().optional(),
|
projectUserPrivilegeId: z.string().uuid().nullable().optional(),
|
||||||
requestedBy: z.string().uuid(),
|
groupProjectUserPrivilegeId: z.string().uuid().nullable().optional(),
|
||||||
|
requestedByUserId: z.string().uuid(),
|
||||||
|
projectMembershipId: z.string().uuid().nullable().optional(),
|
||||||
|
groupMembershipId: z.string().uuid().nullable().optional(),
|
||||||
isTemporary: z.boolean(),
|
isTemporary: z.boolean(),
|
||||||
temporaryRange: z.string().nullable().optional(),
|
temporaryRange: z.string().nullable().optional(),
|
||||||
permissions: z.unknown(),
|
permissions: z.unknown(),
|
||||||
|
@@ -0,0 +1,32 @@
|
|||||||
|
// Code generated by automation script, DO NOT EDIT.
|
||||||
|
// Automated by pulling database and generating zod schema
|
||||||
|
// To update. Just run npm run generate:schema
|
||||||
|
// Written by akhilmhdh.
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const GroupProjectUserAdditionalPrivilegeSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
slug: z.string(),
|
||||||
|
groupProjectMembershipId: z.string().uuid(),
|
||||||
|
requestedByUserId: z.string().uuid(),
|
||||||
|
isTemporary: z.boolean().default(false),
|
||||||
|
temporaryMode: z.string().nullable().optional(),
|
||||||
|
temporaryRange: z.string().nullable().optional(),
|
||||||
|
temporaryAccessStartTime: z.date().nullable().optional(),
|
||||||
|
temporaryAccessEndTime: z.date().nullable().optional(),
|
||||||
|
permissions: z.unknown(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TGroupProjectUserAdditionalPrivilege = z.infer<typeof GroupProjectUserAdditionalPrivilegeSchema>;
|
||||||
|
export type TGroupProjectUserAdditionalPrivilegeInsert = Omit<
|
||||||
|
z.input<typeof GroupProjectUserAdditionalPrivilegeSchema>,
|
||||||
|
TImmutableDBKeys
|
||||||
|
>;
|
||||||
|
export type TGroupProjectUserAdditionalPrivilegeUpdate = Partial<
|
||||||
|
Omit<z.input<typeof GroupProjectUserAdditionalPrivilegeSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
@@ -1,27 +0,0 @@
|
|||||||
// Code generated by automation script, DO NOT EDIT.
|
|
||||||
// Automated by pulling database and generating zod schema
|
|
||||||
// To update. Just run npm run generate:schema
|
|
||||||
// Written by akhilmhdh.
|
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
|
||||||
|
|
||||||
export const IdentityAwsAuthsSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
accessTokenTTL: z.coerce.number().default(7200),
|
|
||||||
accessTokenMaxTTL: z.coerce.number().default(7200),
|
|
||||||
accessTokenNumUsesLimit: z.coerce.number().default(0),
|
|
||||||
accessTokenTrustedIps: z.unknown(),
|
|
||||||
createdAt: z.date(),
|
|
||||||
updatedAt: z.date(),
|
|
||||||
identityId: z.string().uuid(),
|
|
||||||
type: z.string(),
|
|
||||||
stsEndpoint: z.string(),
|
|
||||||
allowedPrincipalArns: z.string(),
|
|
||||||
allowedAccountIds: z.string()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TIdentityAwsAuths = z.infer<typeof IdentityAwsAuthsSchema>;
|
|
||||||
export type TIdentityAwsAuthsInsert = Omit<z.input<typeof IdentityAwsAuthsSchema>, TImmutableDBKeys>;
|
|
||||||
export type TIdentityAwsAuthsUpdate = Partial<Omit<z.input<typeof IdentityAwsAuthsSchema>, TImmutableDBKeys>>;
|
|
@@ -1,26 +0,0 @@
|
|||||||
// Code generated by automation script, DO NOT EDIT.
|
|
||||||
// Automated by pulling database and generating zod schema
|
|
||||||
// To update. Just run npm run generate:schema
|
|
||||||
// Written by akhilmhdh.
|
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
|
||||||
|
|
||||||
export const IdentityAzureAuthsSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
accessTokenTTL: z.coerce.number().default(7200),
|
|
||||||
accessTokenMaxTTL: z.coerce.number().default(7200),
|
|
||||||
accessTokenNumUsesLimit: z.coerce.number().default(0),
|
|
||||||
accessTokenTrustedIps: z.unknown(),
|
|
||||||
createdAt: z.date(),
|
|
||||||
updatedAt: z.date(),
|
|
||||||
identityId: z.string().uuid(),
|
|
||||||
tenantId: z.string(),
|
|
||||||
resource: z.string(),
|
|
||||||
allowedServicePrincipalIds: z.string()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TIdentityAzureAuths = z.infer<typeof IdentityAzureAuthsSchema>;
|
|
||||||
export type TIdentityAzureAuthsInsert = Omit<z.input<typeof IdentityAzureAuthsSchema>, TImmutableDBKeys>;
|
|
||||||
export type TIdentityAzureAuthsUpdate = Partial<Omit<z.input<typeof IdentityAzureAuthsSchema>, TImmutableDBKeys>>;
|
|
@@ -1,27 +0,0 @@
|
|||||||
// Code generated by automation script, DO NOT EDIT.
|
|
||||||
// Automated by pulling database and generating zod schema
|
|
||||||
// To update. Just run npm run generate:schema
|
|
||||||
// Written by akhilmhdh.
|
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
|
||||||
|
|
||||||
export const IdentityGcpAuthsSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
accessTokenTTL: z.coerce.number().default(7200),
|
|
||||||
accessTokenMaxTTL: z.coerce.number().default(7200),
|
|
||||||
accessTokenNumUsesLimit: z.coerce.number().default(0),
|
|
||||||
accessTokenTrustedIps: z.unknown(),
|
|
||||||
createdAt: z.date(),
|
|
||||||
updatedAt: z.date(),
|
|
||||||
identityId: z.string().uuid(),
|
|
||||||
type: z.string(),
|
|
||||||
allowedServiceAccounts: z.string(),
|
|
||||||
allowedProjects: z.string(),
|
|
||||||
allowedZones: z.string()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TIdentityGcpAuths = z.infer<typeof IdentityGcpAuthsSchema>;
|
|
||||||
export type TIdentityGcpAuthsInsert = Omit<z.input<typeof IdentityGcpAuthsSchema>, TImmutableDBKeys>;
|
|
||||||
export type TIdentityGcpAuthsUpdate = Partial<Omit<z.input<typeof IdentityGcpAuthsSchema>, TImmutableDBKeys>>;
|
|
@@ -1,35 +0,0 @@
|
|||||||
// Code generated by automation script, DO NOT EDIT.
|
|
||||||
// Automated by pulling database and generating zod schema
|
|
||||||
// To update. Just run npm run generate:schema
|
|
||||||
// Written by akhilmhdh.
|
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
|
||||||
|
|
||||||
export const IdentityKubernetesAuthsSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
accessTokenTTL: z.coerce.number().default(7200),
|
|
||||||
accessTokenMaxTTL: z.coerce.number().default(7200),
|
|
||||||
accessTokenNumUsesLimit: z.coerce.number().default(0),
|
|
||||||
accessTokenTrustedIps: z.unknown(),
|
|
||||||
createdAt: z.date(),
|
|
||||||
updatedAt: z.date(),
|
|
||||||
identityId: z.string().uuid(),
|
|
||||||
kubernetesHost: z.string(),
|
|
||||||
encryptedCaCert: z.string(),
|
|
||||||
caCertIV: z.string(),
|
|
||||||
caCertTag: z.string(),
|
|
||||||
encryptedTokenReviewerJwt: z.string(),
|
|
||||||
tokenReviewerJwtIV: z.string(),
|
|
||||||
tokenReviewerJwtTag: z.string(),
|
|
||||||
allowedNamespaces: z.string(),
|
|
||||||
allowedNames: z.string(),
|
|
||||||
allowedAudience: z.string()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TIdentityKubernetesAuths = z.infer<typeof IdentityKubernetesAuthsSchema>;
|
|
||||||
export type TIdentityKubernetesAuthsInsert = Omit<z.input<typeof IdentityKubernetesAuthsSchema>, TImmutableDBKeys>;
|
|
||||||
export type TIdentityKubernetesAuthsUpdate = Partial<
|
|
||||||
Omit<z.input<typeof IdentityKubernetesAuthsSchema>, TImmutableDBKeys>
|
|
||||||
>;
|
|
@@ -14,13 +14,10 @@ export * from "./git-app-install-sessions";
|
|||||||
export * from "./git-app-org";
|
export * from "./git-app-org";
|
||||||
export * from "./group-project-membership-roles";
|
export * from "./group-project-membership-roles";
|
||||||
export * from "./group-project-memberships";
|
export * from "./group-project-memberships";
|
||||||
|
export * from "./group-project-user-additional-privilege";
|
||||||
export * from "./groups";
|
export * from "./groups";
|
||||||
export * from "./identities";
|
export * from "./identities";
|
||||||
export * from "./identity-access-tokens";
|
export * from "./identity-access-tokens";
|
||||||
export * from "./identity-aws-auths";
|
|
||||||
export * from "./identity-azure-auths";
|
|
||||||
export * from "./identity-gcp-auths";
|
|
||||||
export * from "./identity-kubernetes-auths";
|
|
||||||
export * from "./identity-org-memberships";
|
export * from "./identity-org-memberships";
|
||||||
export * from "./identity-project-additional-privilege";
|
export * from "./identity-project-additional-privilege";
|
||||||
export * from "./identity-project-membership-role";
|
export * from "./identity-project-membership-role";
|
||||||
@@ -30,9 +27,6 @@ export * from "./identity-universal-auths";
|
|||||||
export * from "./incident-contacts";
|
export * from "./incident-contacts";
|
||||||
export * from "./integration-auths";
|
export * from "./integration-auths";
|
||||||
export * from "./integrations";
|
export * from "./integrations";
|
||||||
export * from "./kms-key-versions";
|
|
||||||
export * from "./kms-keys";
|
|
||||||
export * from "./kms-root-config";
|
|
||||||
export * from "./ldap-configs";
|
export * from "./ldap-configs";
|
||||||
export * from "./ldap-group-maps";
|
export * from "./ldap-group-maps";
|
||||||
export * from "./models";
|
export * from "./models";
|
||||||
@@ -60,11 +54,9 @@ export * from "./secret-blind-indexes";
|
|||||||
export * from "./secret-folder-versions";
|
export * from "./secret-folder-versions";
|
||||||
export * from "./secret-folders";
|
export * from "./secret-folders";
|
||||||
export * from "./secret-imports";
|
export * from "./secret-imports";
|
||||||
export * from "./secret-references";
|
|
||||||
export * from "./secret-rotation-outputs";
|
export * from "./secret-rotation-outputs";
|
||||||
export * from "./secret-rotations";
|
export * from "./secret-rotations";
|
||||||
export * from "./secret-scanning-git-risks";
|
export * from "./secret-scanning-git-risks";
|
||||||
export * from "./secret-sharing";
|
|
||||||
export * from "./secret-snapshot-folders";
|
export * from "./secret-snapshot-folders";
|
||||||
export * from "./secret-snapshot-secrets";
|
export * from "./secret-snapshot-secrets";
|
||||||
export * from "./secret-snapshots";
|
export * from "./secret-snapshots";
|
||||||
|
@@ -28,10 +28,7 @@ export const IntegrationsSchema = z.object({
|
|||||||
secretPath: z.string().default("/"),
|
secretPath: z.string().default("/"),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
lastUsed: z.date().nullable().optional(),
|
lastUsed: z.date().nullable().optional()
|
||||||
isSynced: z.boolean().nullable().optional(),
|
|
||||||
syncMessage: z.string().nullable().optional(),
|
|
||||||
lastSyncJobId: z.string().nullable().optional()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIntegrations = z.infer<typeof IntegrationsSchema>;
|
export type TIntegrations = z.infer<typeof IntegrationsSchema>;
|
||||||
|
@@ -1,21 +0,0 @@
|
|||||||
// Code generated by automation script, DO NOT EDIT.
|
|
||||||
// Automated by pulling database and generating zod schema
|
|
||||||
// To update. Just run npm run generate:schema
|
|
||||||
// Written by akhilmhdh.
|
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { zodBuffer } from "@app/lib/zod";
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
|
||||||
|
|
||||||
export const KmsKeyVersionsSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
encryptedKey: zodBuffer,
|
|
||||||
version: z.number(),
|
|
||||||
kmsKeyId: z.string().uuid()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TKmsKeyVersions = z.infer<typeof KmsKeyVersionsSchema>;
|
|
||||||
export type TKmsKeyVersionsInsert = Omit<z.input<typeof KmsKeyVersionsSchema>, TImmutableDBKeys>;
|
|
||||||
export type TKmsKeyVersionsUpdate = Partial<Omit<z.input<typeof KmsKeyVersionsSchema>, TImmutableDBKeys>>;
|
|
@@ -1,26 +0,0 @@
|
|||||||
// Code generated by automation script, DO NOT EDIT.
|
|
||||||
// Automated by pulling database and generating zod schema
|
|
||||||
// To update. Just run npm run generate:schema
|
|
||||||
// Written by akhilmhdh.
|
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { zodBuffer } from "@app/lib/zod";
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
|
||||||
|
|
||||||
export const KmsKeysSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
encryptedKey: zodBuffer,
|
|
||||||
encryptionAlgorithm: z.string(),
|
|
||||||
version: z.number().default(1),
|
|
||||||
description: z.string().nullable().optional(),
|
|
||||||
isDisabled: z.boolean().default(false).nullable().optional(),
|
|
||||||
isReserved: z.boolean().default(true).nullable().optional(),
|
|
||||||
projectId: z.string().nullable().optional(),
|
|
||||||
orgId: z.string().uuid().nullable().optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TKmsKeys = z.infer<typeof KmsKeysSchema>;
|
|
||||||
export type TKmsKeysInsert = Omit<z.input<typeof KmsKeysSchema>, TImmutableDBKeys>;
|
|
||||||
export type TKmsKeysUpdate = Partial<Omit<z.input<typeof KmsKeysSchema>, TImmutableDBKeys>>;
|
|
@@ -1,19 +0,0 @@
|
|||||||
// Code generated by automation script, DO NOT EDIT.
|
|
||||||
// Automated by pulling database and generating zod schema
|
|
||||||
// To update. Just run npm run generate:schema
|
|
||||||
// Written by akhilmhdh.
|
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { zodBuffer } from "@app/lib/zod";
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
|
||||||
|
|
||||||
export const KmsRootConfigSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
encryptedRootKey: zodBuffer
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TKmsRootConfig = z.infer<typeof KmsRootConfigSchema>;
|
|
||||||
export type TKmsRootConfigInsert = Omit<z.input<typeof KmsRootConfigSchema>, TImmutableDBKeys>;
|
|
||||||
export type TKmsRootConfigUpdate = Partial<Omit<z.input<typeof KmsRootConfigSchema>, TImmutableDBKeys>>;
|
|
@@ -25,11 +25,10 @@ export enum TableName {
|
|||||||
ProjectMembership = "project_memberships",
|
ProjectMembership = "project_memberships",
|
||||||
ProjectRoles = "project_roles",
|
ProjectRoles = "project_roles",
|
||||||
ProjectUserAdditionalPrivilege = "project_user_additional_privilege",
|
ProjectUserAdditionalPrivilege = "project_user_additional_privilege",
|
||||||
|
GroupProjectUserAdditionalPrivilege = "group_project_user_additional_privilege",
|
||||||
ProjectUserMembershipRole = "project_user_membership_roles",
|
ProjectUserMembershipRole = "project_user_membership_roles",
|
||||||
ProjectKeys = "project_keys",
|
ProjectKeys = "project_keys",
|
||||||
Secret = "secrets",
|
Secret = "secrets",
|
||||||
SecretReference = "secret_references",
|
|
||||||
SecretSharing = "secret_sharing",
|
|
||||||
SecretBlindIndex = "secret_blind_indexes",
|
SecretBlindIndex = "secret_blind_indexes",
|
||||||
SecretVersion = "secret_versions",
|
SecretVersion = "secret_versions",
|
||||||
SecretFolder = "secret_folders",
|
SecretFolder = "secret_folders",
|
||||||
@@ -46,11 +45,7 @@ export enum TableName {
|
|||||||
Identity = "identities",
|
Identity = "identities",
|
||||||
IdentityAccessToken = "identity_access_tokens",
|
IdentityAccessToken = "identity_access_tokens",
|
||||||
IdentityUniversalAuth = "identity_universal_auths",
|
IdentityUniversalAuth = "identity_universal_auths",
|
||||||
IdentityKubernetesAuth = "identity_kubernetes_auths",
|
|
||||||
IdentityGcpAuth = "identity_gcp_auths",
|
|
||||||
IdentityAzureAuth = "identity_azure_auths",
|
|
||||||
IdentityUaClientSecret = "identity_ua_client_secrets",
|
IdentityUaClientSecret = "identity_ua_client_secrets",
|
||||||
IdentityAwsAuth = "identity_aws_auths",
|
|
||||||
IdentityOrgMembership = "identity_org_memberships",
|
IdentityOrgMembership = "identity_org_memberships",
|
||||||
IdentityProjectMembership = "identity_project_memberships",
|
IdentityProjectMembership = "identity_project_memberships",
|
||||||
IdentityProjectMembershipRole = "identity_project_membership_role",
|
IdentityProjectMembershipRole = "identity_project_membership_role",
|
||||||
@@ -81,11 +76,7 @@ export enum TableName {
|
|||||||
DynamicSecretLease = "dynamic_secret_leases",
|
DynamicSecretLease = "dynamic_secret_leases",
|
||||||
// junction tables with tags
|
// junction tables with tags
|
||||||
JnSecretTag = "secret_tag_junction",
|
JnSecretTag = "secret_tag_junction",
|
||||||
SecretVersionTag = "secret_version_tag_junction",
|
SecretVersionTag = "secret_version_tag_junction"
|
||||||
// KMS Service
|
|
||||||
KmsServerRootConfig = "kms_root_config",
|
|
||||||
KmsKey = "kms_keys",
|
|
||||||
KmsKeyVersion = "kms_key_versions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
|
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
|
||||||
@@ -152,9 +143,5 @@ export enum ProjectUpgradeStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum IdentityAuthMethod {
|
export enum IdentityAuthMethod {
|
||||||
Univeral = "universal-auth",
|
Univeral = "universal-auth"
|
||||||
KUBERNETES_AUTH = "kubernetes-auth",
|
|
||||||
GCP_AUTH = "gcp-auth",
|
|
||||||
AWS_AUTH = "aws-auth",
|
|
||||||
AZURE_AUTH = "azure-auth"
|
|
||||||
}
|
}
|
||||||
|
@@ -9,10 +9,11 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
|
|
||||||
export const SecretApprovalPoliciesApproversSchema = z.object({
|
export const SecretApprovalPoliciesApproversSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
approverId: z.string().uuid(),
|
approverId: z.string().uuid().nullable().optional(),
|
||||||
policyId: z.string().uuid(),
|
policyId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date(),
|
||||||
|
approverUserId: z.string().uuid().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretApprovalPoliciesApprovers = z.infer<typeof SecretApprovalPoliciesApproversSchema>;
|
export type TSecretApprovalPoliciesApprovers = z.infer<typeof SecretApprovalPoliciesApproversSchema>;
|
||||||
|
@@ -9,11 +9,12 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
|
|
||||||
export const SecretApprovalRequestsReviewersSchema = z.object({
|
export const SecretApprovalRequestsReviewersSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
member: z.string().uuid(),
|
member: z.string().uuid().nullable().optional(),
|
||||||
status: z.string(),
|
status: z.string(),
|
||||||
requestId: z.string().uuid(),
|
requestId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date(),
|
||||||
|
memberUserId: z.string().uuid().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretApprovalRequestsReviewers = z.infer<typeof SecretApprovalRequestsReviewersSchema>;
|
export type TSecretApprovalRequestsReviewers = z.infer<typeof SecretApprovalRequestsReviewersSchema>;
|
||||||
|
@@ -16,10 +16,11 @@ export const SecretApprovalRequestsSchema = z.object({
|
|||||||
slug: z.string(),
|
slug: z.string(),
|
||||||
folderId: z.string().uuid(),
|
folderId: z.string().uuid(),
|
||||||
statusChangeBy: z.string().uuid().nullable().optional(),
|
statusChangeBy: z.string().uuid().nullable().optional(),
|
||||||
committerId: z.string().uuid(),
|
committerId: z.string().uuid().nullable().optional(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
isReplicated: z.boolean().nullable().optional()
|
statusChangeByUserId: z.string().uuid().nullable().optional(),
|
||||||
|
committerUserId: z.string().uuid().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretApprovalRequests = z.infer<typeof SecretApprovalRequestsSchema>;
|
export type TSecretApprovalRequests = z.infer<typeof SecretApprovalRequestsSchema>;
|
||||||
|
@@ -14,8 +14,7 @@ export const SecretFoldersSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
envId: z.string().uuid(),
|
envId: z.string().uuid(),
|
||||||
parentId: z.string().uuid().nullable().optional(),
|
parentId: z.string().uuid().nullable().optional()
|
||||||
isReserved: z.boolean().default(false).nullable().optional()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretFolders = z.infer<typeof SecretFoldersSchema>;
|
export type TSecretFolders = z.infer<typeof SecretFoldersSchema>;
|
||||||
|
@@ -15,12 +15,7 @@ export const SecretImportsSchema = z.object({
|
|||||||
position: z.number(),
|
position: z.number(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
folderId: z.string().uuid(),
|
folderId: z.string().uuid()
|
||||||
isReplication: z.boolean().default(false).nullable().optional(),
|
|
||||||
isReplicationSuccess: z.boolean().nullable().optional(),
|
|
||||||
replicationStatus: z.string().nullable().optional(),
|
|
||||||
lastReplicated: z.date().nullable().optional(),
|
|
||||||
isReserved: z.boolean().default(false).nullable().optional()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretImports = z.infer<typeof SecretImportsSchema>;
|
export type TSecretImports = z.infer<typeof SecretImportsSchema>;
|
||||||
|
@@ -1,21 +0,0 @@
|
|||||||
// Code generated by automation script, DO NOT EDIT.
|
|
||||||
// Automated by pulling database and generating zod schema
|
|
||||||
// To update. Just run npm run generate:schema
|
|
||||||
// Written by akhilmhdh.
|
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
|
||||||
|
|
||||||
export const SecretReferencesSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
environment: z.string(),
|
|
||||||
secretPath: z.string(),
|
|
||||||
secretId: z.string().uuid(),
|
|
||||||
createdAt: z.date(),
|
|
||||||
updatedAt: z.date()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TSecretReferences = z.infer<typeof SecretReferencesSchema>;
|
|
||||||
export type TSecretReferencesInsert = Omit<z.input<typeof SecretReferencesSchema>, TImmutableDBKeys>;
|
|
||||||
export type TSecretReferencesUpdate = Partial<Omit<z.input<typeof SecretReferencesSchema>, TImmutableDBKeys>>;
|
|
@@ -1,26 +0,0 @@
|
|||||||
// Code generated by automation script, DO NOT EDIT.
|
|
||||||
// Automated by pulling database and generating zod schema
|
|
||||||
// To update. Just run npm run generate:schema
|
|
||||||
// Written by akhilmhdh.
|
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
|
||||||
|
|
||||||
export const SecretSharingSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
encryptedValue: z.string(),
|
|
||||||
iv: z.string(),
|
|
||||||
tag: z.string(),
|
|
||||||
hashedHex: z.string(),
|
|
||||||
expiresAt: z.date(),
|
|
||||||
userId: z.string().uuid(),
|
|
||||||
orgId: z.string().uuid(),
|
|
||||||
createdAt: z.date(),
|
|
||||||
updatedAt: z.date(),
|
|
||||||
expiresAfterViews: z.number().nullable().optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TSecretSharing = z.infer<typeof SecretSharingSchema>;
|
|
||||||
export type TSecretSharingInsert = Omit<z.input<typeof SecretSharingSchema>, TImmutableDBKeys>;
|
|
||||||
export type TSecretSharingUpdate = Partial<Omit<z.input<typeof SecretSharingSchema>, TImmutableDBKeys>>;
|
|
@@ -14,9 +14,7 @@ export const SuperAdminSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
allowedSignUpDomain: z.string().nullable().optional(),
|
allowedSignUpDomain: z.string().nullable().optional(),
|
||||||
instanceId: z.string().uuid().default("00000000-0000-0000-0000-000000000000"),
|
instanceId: z.string().uuid().default("00000000-0000-0000-0000-000000000000")
|
||||||
trustSamlEmails: z.boolean().default(false).nullable().optional(),
|
|
||||||
trustLdapEmails: z.boolean().default(false).nullable().optional()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
||||||
|
@@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
export const UserAliasesSchema = z.object({
|
export const UserAliasesSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
userId: z.string().uuid(),
|
userId: z.string().uuid(),
|
||||||
username: z.string().nullable().optional(),
|
username: z.string(),
|
||||||
aliasType: z.string(),
|
aliasType: z.string(),
|
||||||
externalId: z.string(),
|
externalId: z.string(),
|
||||||
emails: z.string().array().nullable().optional(),
|
emails: z.string().array().nullable().optional(),
|
||||||
|
@@ -21,11 +21,7 @@ export const UsersSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
isGhost: z.boolean().default(false),
|
isGhost: z.boolean().default(false),
|
||||||
username: z.string(),
|
username: z.string()
|
||||||
isEmailVerified: z.boolean().default(false).nullable().optional(),
|
|
||||||
consecutiveFailedMfaAttempts: z.number().default(0).nullable().optional(),
|
|
||||||
isLocked: z.boolean().default(false).nullable().optional(),
|
|
||||||
temporaryLockDateEnd: z.date().nullable().optional()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TUsers = z.infer<typeof UsersSchema>;
|
export type TUsers = z.infer<typeof UsersSchema>;
|
||||||
|
@@ -53,7 +53,9 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
approvals: sapPubSchema.extend({ approvers: z.string().array(), secretPath: z.string().optional() }).array()
|
approvals: sapPubSchema
|
||||||
|
.extend({ approvers: z.string().nullish().array(), secretPath: z.string().optional() })
|
||||||
|
.array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -74,7 +74,7 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
projectSlug: z.string().trim(),
|
projectSlug: z.string().trim(),
|
||||||
authorProjectMembershipId: z.string().trim().optional(),
|
authorUserId: z.string().trim().optional(),
|
||||||
envSlug: z.string().trim().optional()
|
envSlug: z.string().trim().optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
@@ -84,7 +84,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
isApproved: z.boolean(),
|
isApproved: z.boolean(),
|
||||||
privilege: z
|
privilege: z
|
||||||
.object({
|
.object({
|
||||||
membershipId: z.string(),
|
projectMembershipId: z.string().nullish(),
|
||||||
|
groupMembershipId: z.string().nullish(),
|
||||||
isTemporary: z.boolean(),
|
isTemporary: z.boolean(),
|
||||||
temporaryMode: z.string().nullish(),
|
temporaryMode: z.string().nullish(),
|
||||||
temporaryRange: z.string().nullish(),
|
temporaryRange: z.string().nullish(),
|
||||||
@@ -115,8 +116,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { requests } = await server.services.accessApprovalRequest.listApprovalRequests({
|
const { requests } = await server.services.accessApprovalRequest.listApprovalRequests({
|
||||||
projectSlug: req.query.projectSlug,
|
projectSlug: req.query.projectSlug,
|
||||||
authorProjectMembershipId: req.query.authorProjectMembershipId,
|
|
||||||
envSlug: req.query.envSlug,
|
envSlug: req.query.envSlug,
|
||||||
|
authorUserId: req.query.authorUserId,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
@@ -127,6 +128,37 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/:requestId",
|
||||||
|
method: "DELETE",
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
requestId: z.string().trim()
|
||||||
|
}),
|
||||||
|
querystring: z.object({
|
||||||
|
projectSlug: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
request: AccessApprovalRequestsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { request } = await server.services.accessApprovalRequest.deleteAccessApprovalRequest({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
requestId: req.params.requestId,
|
||||||
|
projectSlug: req.query.projectSlug
|
||||||
|
});
|
||||||
|
|
||||||
|
return { request };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:requestId/review",
|
url: "/:requestId/review",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@@ -1,19 +1,16 @@
|
|||||||
import { packRules } from "@casl/ability/extra";
|
import { MongoAbility, RawRuleOf } from "@casl/ability";
|
||||||
|
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { IdentityProjectAdditionalPrivilegeSchema } from "@app/db/schemas";
|
||||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
||||||
|
import { ProjectPermissionSet } from "@app/ee/services/permission/project-permission";
|
||||||
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import {
|
|
||||||
ProjectPermissionSchema,
|
|
||||||
ProjectSpecificPrivilegePermissionSchema,
|
|
||||||
SanitizedIdentityPrivilegeSchema
|
|
||||||
} from "@app/server/routes/sanitizedSchemas";
|
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
||||||
@@ -44,33 +41,16 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||||
permissions: ProjectPermissionSchema.array()
|
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
|
||||||
.optional(),
|
|
||||||
privilegePermission: ProjectSpecificPrivilegePermissionSchema.describe(
|
|
||||||
IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.privilegePermission
|
|
||||||
).optional()
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: SanitizedIdentityPrivilegeSchema
|
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { permissions, privilegePermission } = req.body;
|
|
||||||
if (!permissions && !privilegePermission) {
|
|
||||||
throw new BadRequestError({ message: "Permission or privilegePermission must be provided" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const permission = privilegePermission
|
|
||||||
? privilegePermission.actions.map((action) => ({
|
|
||||||
action,
|
|
||||||
subject: privilegePermission.subject,
|
|
||||||
conditions: privilegePermission.conditions
|
|
||||||
}))
|
|
||||||
: permissions!;
|
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
|
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
@@ -79,7 +59,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
...req.body,
|
...req.body,
|
||||||
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
||||||
isTemporary: false,
|
isTemporary: false,
|
||||||
permissions: JSON.stringify(packRules(permission))
|
permissions: JSON.stringify(packRules(req.body.permissions))
|
||||||
});
|
});
|
||||||
return { privilege };
|
return { privilege };
|
||||||
}
|
}
|
||||||
@@ -112,12 +92,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||||
permissions: ProjectPermissionSchema.array()
|
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions),
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
|
||||||
.optional(),
|
|
||||||
privilegePermission: ProjectSpecificPrivilegePermissionSchema.describe(
|
|
||||||
IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.privilegePermission
|
|
||||||
).optional(),
|
|
||||||
temporaryMode: z
|
temporaryMode: z
|
||||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
||||||
@@ -132,25 +107,12 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: SanitizedIdentityPrivilegeSchema
|
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { permissions, privilegePermission } = req.body;
|
|
||||||
if (!permissions && !privilegePermission) {
|
|
||||||
throw new BadRequestError({ message: "Permission or privilegePermission must be provided" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const permission = privilegePermission
|
|
||||||
? privilegePermission.actions.map((action) => ({
|
|
||||||
action,
|
|
||||||
subject: privilegePermission.subject,
|
|
||||||
conditions: privilegePermission.conditions
|
|
||||||
}))
|
|
||||||
: permissions!;
|
|
||||||
|
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
|
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
@@ -159,7 +121,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
...req.body,
|
...req.body,
|
||||||
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
||||||
isTemporary: true,
|
isTemporary: true,
|
||||||
permissions: JSON.stringify(packRules(permission))
|
permissions: JSON.stringify(packRules(req.body.permissions))
|
||||||
});
|
});
|
||||||
return { privilege };
|
return { privilege };
|
||||||
}
|
}
|
||||||
@@ -195,17 +157,14 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
message: "Slug must be a valid slug"
|
message: "Slug must be a valid slug"
|
||||||
})
|
})
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.newSlug),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.newSlug),
|
||||||
permissions: ProjectPermissionSchema.array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.permissions),
|
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.permissions),
|
||||||
privilegePermission: ProjectSpecificPrivilegePermissionSchema.describe(
|
|
||||||
IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.privilegePermission
|
|
||||||
).optional(),
|
|
||||||
isTemporary: z.boolean().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
|
isTemporary: z.boolean().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
|
||||||
temporaryMode: z
|
temporaryMode: z
|
||||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
|
||||||
temporaryRange: z
|
temporaryRange: z
|
||||||
.string()
|
.string()
|
||||||
.refine((val) => typeof val === "undefined" || ms(val) > 0, "Temporary range must be a positive number")
|
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryRange),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryRange),
|
||||||
temporaryAccessStartTime: z
|
temporaryAccessStartTime: z
|
||||||
.string()
|
.string()
|
||||||
@@ -216,24 +175,13 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: SanitizedIdentityPrivilegeSchema
|
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { permissions, privilegePermission, ...updatedInfo } = req.body.privilegeDetails;
|
const updatedInfo = req.body.privilegeDetails;
|
||||||
if (!permissions && !privilegePermission) {
|
|
||||||
throw new BadRequestError({ message: "Permission or privilegePermission must be provided" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const permission = privilegePermission
|
|
||||||
? privilegePermission.actions.map((action) => ({
|
|
||||||
action,
|
|
||||||
subject: privilegePermission.subject,
|
|
||||||
conditions: privilegePermission.conditions
|
|
||||||
}))
|
|
||||||
: permissions!;
|
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilege.updateBySlug({
|
const privilege = await server.services.identityProjectAdditionalPrivilege.updateBySlug({
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
@@ -244,7 +192,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
projectSlug: req.body.projectSlug,
|
projectSlug: req.body.projectSlug,
|
||||||
data: {
|
data: {
|
||||||
...updatedInfo,
|
...updatedInfo,
|
||||||
permissions: permission ? JSON.stringify(packRules(permission)) : undefined
|
permissions: updatedInfo?.permissions ? JSON.stringify(packRules(updatedInfo.permissions)) : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return { privilege };
|
return { privilege };
|
||||||
@@ -271,7 +219,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: SanitizedIdentityPrivilegeSchema
|
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -312,7 +260,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: SanitizedIdentityPrivilegeSchema
|
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -345,11 +293,16 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
],
|
],
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.identityId),
|
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.identityId),
|
||||||
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.projectSlug)
|
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.projectSlug),
|
||||||
|
unpacked: z
|
||||||
|
.enum(["false", "true"])
|
||||||
|
.transform((el) => el === "true")
|
||||||
|
.default("true")
|
||||||
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.unpacked)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privileges: SanitizedIdentityPrivilegeSchema.array()
|
privileges: IdentityProjectAdditionalPrivilegeSchema.array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -362,9 +315,15 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
...req.query
|
...req.query
|
||||||
});
|
});
|
||||||
return {
|
if (req.query.unpacked) {
|
||||||
privileges
|
return {
|
||||||
};
|
privileges: privileges.map(({ permissions, ...el }) => ({
|
||||||
|
...el,
|
||||||
|
permissions: unpackRules(permissions as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[])
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { privileges };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
|
import { registerAuditLogStreamRouter } from "./audit-log-stream-router";
|
||||||
import { registerAccessApprovalPolicyRouter } from "./access-approval-policy-router";
|
import { registerAccessApprovalPolicyRouter } from "./access-approval-policy-router";
|
||||||
import { registerAccessApprovalRequestRouter } from "./access-approval-request-router";
|
import { registerAccessApprovalRequestRouter } from "./access-approval-request-router";
|
||||||
import { registerAuditLogStreamRouter } from "./audit-log-stream-router";
|
|
||||||
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
|
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
|
||||||
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
|
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
|
||||||
import { registerGroupRouter } from "./group-router";
|
import { registerGroupRouter } from "./group-router";
|
||||||
|
@@ -18,7 +18,6 @@ import { LdapConfigsSchema, LdapGroupMapsSchema } from "@app/db/schemas";
|
|||||||
import { TLDAPConfig } from "@app/ee/services/ldap-config/ldap-config-types";
|
import { TLDAPConfig } from "@app/ee/services/ldap-config/ldap-config-types";
|
||||||
import { isValidLdapFilter, searchGroups } from "@app/ee/services/ldap-config/ldap-fns";
|
import { isValidLdapFilter, searchGroups } from "@app/ee/services/ldap-config/ldap-fns";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
@@ -53,7 +52,6 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
async (req: IncomingMessage, user, cb) => {
|
async (req: IncomingMessage, user, cb) => {
|
||||||
try {
|
try {
|
||||||
if (!user.email) throw new BadRequestError({ message: "Invalid request. Missing email." });
|
|
||||||
const ldapConfig = (req as unknown as FastifyRequest).ldapConfig as TLDAPConfig;
|
const ldapConfig = (req as unknown as FastifyRequest).ldapConfig as TLDAPConfig;
|
||||||
|
|
||||||
let groups: { dn: string; cn: string }[] | undefined;
|
let groups: { dn: string; cn: string }[] | undefined;
|
||||||
@@ -76,7 +74,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
username: user.uid,
|
username: user.uid,
|
||||||
firstName: user.givenName ?? user.cn ?? "",
|
firstName: user.givenName ?? user.cn ?? "",
|
||||||
lastName: user.sn ?? "",
|
lastName: user.sn ?? "",
|
||||||
email: user.mail,
|
emails: user.mail ? [user.mail] : [],
|
||||||
groups,
|
groups,
|
||||||
relayState: ((req as unknown as FastifyRequest).body as { RelayState?: string }).RelayState,
|
relayState: ((req as unknown as FastifyRequest).body as { RelayState?: string }).RelayState,
|
||||||
orgId: (req as unknown as FastifyRequest).ldapConfig.organization
|
orgId: (req as unknown as FastifyRequest).ldapConfig.organization
|
||||||
|
@@ -23,7 +23,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
.min(1)
|
.min(1)
|
||||||
.trim()
|
.trim()
|
||||||
.refine(
|
.refine(
|
||||||
(val) => !Object.values(OrgMembershipRole).includes(val as OrgMembershipRole),
|
(val) => !Object.keys(OrgMembershipRole).includes(val),
|
||||||
"Please choose a different slug, the slug you have entered is reserved"
|
"Please choose a different slug, the slug you have entered is reserved"
|
||||||
)
|
)
|
||||||
.refine((v) => slugify(v) === v, {
|
.refine((v) => slugify(v) === v, {
|
||||||
|
@@ -1,232 +1,146 @@
|
|||||||
import { packRules } from "@casl/ability/extra";
|
|
||||||
import slugify from "@sindresorhus/slugify";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ProjectMembershipRole, ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
|
import { ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
|
||||||
import { PROJECT_ROLE } from "@app/lib/api-docs";
|
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { ProjectPermissionSchema, SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas";
|
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/:projectSlug/roles",
|
url: "/:projectId/roles",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: writeLimit
|
rateLimit: writeLimit
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
description: "Create a project role",
|
|
||||||
security: [
|
|
||||||
{
|
|
||||||
bearerAuth: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectSlug: z.string().trim().describe(PROJECT_ROLE.CREATE.projectSlug)
|
projectId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
slug: z
|
slug: z.string().trim(),
|
||||||
.string()
|
name: z.string().trim(),
|
||||||
.toLowerCase()
|
description: z.string().trim().optional(),
|
||||||
.trim()
|
permissions: z.any().array()
|
||||||
.min(1)
|
|
||||||
.refine(
|
|
||||||
(val) => !Object.values(ProjectMembershipRole).includes(val as ProjectMembershipRole),
|
|
||||||
"Please choose a different slug, the slug you have entered is reserved"
|
|
||||||
)
|
|
||||||
.refine((v) => slugify(v) === v, {
|
|
||||||
message: "Slug must be a valid"
|
|
||||||
})
|
|
||||||
.describe(PROJECT_ROLE.CREATE.slug),
|
|
||||||
name: z.string().min(1).trim().describe(PROJECT_ROLE.CREATE.name),
|
|
||||||
description: z.string().trim().optional().describe(PROJECT_ROLE.CREATE.description),
|
|
||||||
permissions: ProjectPermissionSchema.array().describe(PROJECT_ROLE.CREATE.permissions)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
role: SanitizedRoleSchema
|
role: ProjectRolesSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.projectRole.createRole({
|
const role = await server.services.projectRole.createRole(
|
||||||
actorAuthMethod: req.permission.authMethod,
|
req.permission.type,
|
||||||
actorId: req.permission.id,
|
req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
req.params.projectId,
|
||||||
actor: req.permission.type,
|
req.body,
|
||||||
projectSlug: req.params.projectSlug,
|
req.permission.authMethod,
|
||||||
data: {
|
req.permission.orgId
|
||||||
...req.body,
|
);
|
||||||
permissions: JSON.stringify(packRules(req.body.permissions))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return { role };
|
return { role };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
url: "/:projectSlug/roles/:roleId",
|
url: "/:projectId/roles/:roleId",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: writeLimit
|
rateLimit: writeLimit
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
description: "Update a project role",
|
|
||||||
security: [
|
|
||||||
{
|
|
||||||
bearerAuth: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectSlug: z.string().trim().describe(PROJECT_ROLE.UPDATE.projectSlug),
|
projectId: z.string().trim(),
|
||||||
roleId: z.string().trim().describe(PROJECT_ROLE.UPDATE.roleId)
|
roleId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
slug: z
|
slug: z.string().trim().optional(),
|
||||||
.string()
|
name: z.string().trim().optional(),
|
||||||
.toLowerCase()
|
description: z.string().trim().optional(),
|
||||||
.trim()
|
permissions: z.any().array()
|
||||||
.optional()
|
|
||||||
.describe(PROJECT_ROLE.UPDATE.slug)
|
|
||||||
.refine(
|
|
||||||
(val) =>
|
|
||||||
typeof val === "undefined" ||
|
|
||||||
!Object.values(ProjectMembershipRole).includes(val as ProjectMembershipRole),
|
|
||||||
"Please choose a different slug, the slug you have entered is reserved"
|
|
||||||
)
|
|
||||||
.refine((val) => typeof val === "undefined" || slugify(val) === val, {
|
|
||||||
message: "Slug must be a valid"
|
|
||||||
}),
|
|
||||||
name: z.string().trim().optional().describe(PROJECT_ROLE.UPDATE.name),
|
|
||||||
permissions: ProjectPermissionSchema.array().describe(PROJECT_ROLE.UPDATE.permissions)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
role: SanitizedRoleSchema
|
role: ProjectRolesSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.projectRole.updateRole({
|
const role = await server.services.projectRole.updateRole(
|
||||||
actorAuthMethod: req.permission.authMethod,
|
req.permission.type,
|
||||||
actorId: req.permission.id,
|
req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
req.params.projectId,
|
||||||
actor: req.permission.type,
|
req.params.roleId,
|
||||||
projectSlug: req.params.projectSlug,
|
req.body,
|
||||||
roleId: req.params.roleId,
|
req.permission.authMethod,
|
||||||
data: {
|
req.permission.orgId
|
||||||
...req.body,
|
);
|
||||||
permissions: JSON.stringify(packRules(req.body.permissions))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return { role };
|
return { role };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
url: "/:projectSlug/roles/:roleId",
|
url: "/:projectId/roles/:roleId",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: writeLimit
|
rateLimit: writeLimit
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
description: "Delete a project role",
|
|
||||||
security: [
|
|
||||||
{
|
|
||||||
bearerAuth: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectSlug: z.string().trim().describe(PROJECT_ROLE.DELETE.projectSlug),
|
projectId: z.string().trim(),
|
||||||
roleId: z.string().trim().describe(PROJECT_ROLE.DELETE.roleId)
|
roleId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
role: SanitizedRoleSchema
|
role: ProjectRolesSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.projectRole.deleteRole({
|
const role = await server.services.projectRole.deleteRole(
|
||||||
actorAuthMethod: req.permission.authMethod,
|
req.permission.type,
|
||||||
actorId: req.permission.id,
|
req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
req.params.projectId,
|
||||||
actor: req.permission.type,
|
req.params.roleId,
|
||||||
projectSlug: req.params.projectSlug,
|
req.permission.authMethod,
|
||||||
roleId: req.params.roleId
|
req.permission.orgId
|
||||||
});
|
);
|
||||||
return { role };
|
return { role };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:projectSlug/roles",
|
url: "/:projectId/roles",
|
||||||
config: {
|
|
||||||
rateLimit: readLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
description: "List project role",
|
|
||||||
security: [
|
|
||||||
{
|
|
||||||
bearerAuth: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
params: z.object({
|
|
||||||
projectSlug: z.string().trim().describe(PROJECT_ROLE.LIST.projectSlug)
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
roles: ProjectRolesSchema.omit({ permissions: true }).array()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const roles = await server.services.projectRole.listRoles({
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actor: req.permission.type,
|
|
||||||
projectSlug: req.params.projectSlug
|
|
||||||
});
|
|
||||||
return { roles };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: "GET",
|
|
||||||
url: "/:projectSlug/roles/slug/:slug",
|
|
||||||
config: {
|
config: {
|
||||||
rateLimit: readLimit
|
rateLimit: readLimit
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectSlug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.projectSlug),
|
projectId: z.string().trim()
|
||||||
slug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.roleSlug)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
role: SanitizedRoleSchema
|
data: z.object({
|
||||||
|
roles: ProjectRolesSchema.omit({ permissions: true })
|
||||||
|
.merge(z.object({ permissions: z.unknown() }))
|
||||||
|
.array()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.projectRole.getRoleBySlug({
|
const roles = await server.services.projectRole.listRoles(
|
||||||
actorAuthMethod: req.permission.authMethod,
|
req.permission.type,
|
||||||
actorId: req.permission.id,
|
req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
req.params.projectId,
|
||||||
actor: req.permission.type,
|
req.permission.authMethod,
|
||||||
projectSlug: req.params.projectSlug,
|
req.permission.orgId
|
||||||
roleSlug: req.params.slug
|
);
|
||||||
});
|
return { data: { roles } };
|
||||||
return { role };
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -102,12 +102,12 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
if (!profile) throw new BadRequestError({ message: "Missing profile" });
|
if (!profile) throw new BadRequestError({ message: "Missing profile" });
|
||||||
const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved
|
const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved
|
||||||
|
|
||||||
if (!email || !profile.firstName) {
|
if (!profile.email || !profile.firstName) {
|
||||||
throw new BadRequestError({ message: "Invalid request. Missing email or first name" });
|
throw new BadRequestError({ message: "Invalid request. Missing email or first name" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
|
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
|
||||||
externalId: profile.nameID,
|
username: profile.nameID ?? email,
|
||||||
email,
|
email,
|
||||||
firstName: profile.firstName as string,
|
firstName: profile.firstName as string,
|
||||||
lastName: profile.lastName as string,
|
lastName: profile.lastName as string,
|
||||||
|
@@ -153,7 +153,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const users = await req.server.services.scim.listScimUsers({
|
const users = await req.server.services.scim.listScimUsers({
|
||||||
startIndex: req.query.startIndex,
|
offset: req.query.startIndex,
|
||||||
limit: req.query.count,
|
limit: req.query.count,
|
||||||
filter: req.query.filter,
|
filter: req.query.filter,
|
||||||
orgId: req.permission.orgId
|
orgId: req.permission.orgId
|
||||||
@@ -163,11 +163,11 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/Users/:orgMembershipId",
|
url: "/Users/:userId",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
orgMembershipId: z.string().trim()
|
userId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
201: z.object({
|
201: z.object({
|
||||||
@@ -193,7 +193,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const user = await req.server.services.scim.getScimUser({
|
const user = await req.server.services.scim.getScimUser({
|
||||||
orgMembershipId: req.params.orgMembershipId,
|
userId: req.params.userId,
|
||||||
orgId: req.permission.orgId
|
orgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
return user;
|
return user;
|
||||||
@@ -249,7 +249,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
const primaryEmail = req.body.emails?.find((email) => email.primary)?.value;
|
const primaryEmail = req.body.emails?.find((email) => email.primary)?.value;
|
||||||
|
|
||||||
const user = await req.server.services.scim.createScimUser({
|
const user = await req.server.services.scim.createScimUser({
|
||||||
externalId: req.body.userName,
|
username: req.body.userName,
|
||||||
email: primaryEmail,
|
email: primaryEmail,
|
||||||
firstName: req.body.name.givenName,
|
firstName: req.body.name.givenName,
|
||||||
lastName: req.body.name.familyName,
|
lastName: req.body.name.familyName,
|
||||||
@@ -261,11 +261,11 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/Users/:orgMembershipId",
|
url: "/Users/:userId",
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
orgMembershipId: z.string().trim()
|
userId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({})
|
200: z.object({})
|
||||||
@@ -274,7 +274,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const user = await req.server.services.scim.deleteScimUser({
|
const user = await req.server.services.scim.deleteScimUser({
|
||||||
orgMembershipId: req.params.orgMembershipId,
|
userId: req.params.userId,
|
||||||
orgId: req.permission.orgId
|
orgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -361,7 +361,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const groups = await req.server.services.scim.listScimGroups({
|
const groups = await req.server.services.scim.listScimGroups({
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
startIndex: req.query.startIndex,
|
offset: req.query.startIndex,
|
||||||
limit: req.query.count
|
limit: req.query.count
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -416,10 +416,10 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
displayName: z.string().trim(),
|
displayName: z.string().trim(),
|
||||||
members: z.array(
|
members: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
value: z.string(), // infisical orgMembershipId
|
value: z.string(), // infisical userId
|
||||||
display: z.string()
|
display: z.string()
|
||||||
})
|
})
|
||||||
)
|
) // note: is this where members are added to group?
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -534,11 +534,11 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/Users/:orgMembershipId",
|
url: "/Users/:userId",
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
orgMembershipId: z.string().trim()
|
userId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
schemas: z.array(z.string()),
|
schemas: z.array(z.string()),
|
||||||
@@ -575,7 +575,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const user = await req.server.services.scim.replaceScimUser({
|
const user = await req.server.services.scim.replaceScimUser({
|
||||||
orgMembershipId: req.params.orgMembershipId,
|
userId: req.params.userId,
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
active: req.body.active
|
active: req.body.active
|
||||||
});
|
});
|
||||||
|
@@ -130,7 +130,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
approvals: sapPubSchema.merge(z.object({ approvers: z.string().array() })).array()
|
approvals: sapPubSchema.merge(z.object({ approvers: z.string().nullish().array() })).array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -161,7 +161,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
policy: sapPubSchema.merge(z.object({ approvers: z.string().array() })).optional()
|
policy: sapPubSchema.merge(z.object({ approvers: z.string().nullish().array() })).optional()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -32,20 +32,22 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
approvals: SecretApprovalRequestsSchema.extend({
|
approvals: SecretApprovalRequestsSchema.merge(
|
||||||
// secretPath: z.string(),
|
z.object({
|
||||||
policy: z.object({
|
// secretPath: z.string(),
|
||||||
id: z.string(),
|
policy: z.object({
|
||||||
name: z.string(),
|
id: z.string(),
|
||||||
approvals: z.number(),
|
name: z.string(),
|
||||||
approvers: z.string().array(),
|
approvals: z.number(),
|
||||||
secretPath: z.string().optional().nullable()
|
approvers: z.string().array(),
|
||||||
}),
|
secretPath: z.string().optional().nullable()
|
||||||
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
|
}),
|
||||||
environment: z.string(),
|
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
|
||||||
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
|
environment: z.string(),
|
||||||
approvers: z.string().array()
|
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
|
||||||
}).array()
|
approvers: z.string().array()
|
||||||
|
})
|
||||||
|
).array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -195,7 +197,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
type: isClosing ? EventType.SECRET_APPROVAL_CLOSED : EventType.SECRET_APPROVAL_REOPENED,
|
type: isClosing ? EventType.SECRET_APPROVAL_CLOSED : EventType.SECRET_APPROVAL_REOPENED,
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
metadata: {
|
metadata: {
|
||||||
[isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]: approval.statusChangeBy as string,
|
[isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]: approval.statusChangeByUserId as string,
|
||||||
secretApprovalRequestId: approval.id,
|
secretApprovalRequestId: approval.id,
|
||||||
secretApprovalRequestSlug: approval.slug
|
secretApprovalRequestSlug: approval.slug
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
|
@@ -20,7 +20,7 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.AccessApprovalPolicy}.id`,
|
`${TableName.AccessApprovalPolicy}.id`,
|
||||||
`${TableName.AccessApprovalPolicyApprover}.policyId`
|
`${TableName.AccessApprovalPolicyApprover}.policyId`
|
||||||
)
|
)
|
||||||
.select(tx.ref("approverId").withSchema(TableName.AccessApprovalPolicyApprover))
|
.select(tx.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||||
.select(tx.ref("name").withSchema(TableName.Environment).as("envName"))
|
.select(tx.ref("name").withSchema(TableName.Environment).as("envName"))
|
||||||
.select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
.select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||||
.select(tx.ref("id").withSchema(TableName.Environment).as("envId"))
|
.select(tx.ref("id").withSchema(TableName.Environment).as("envId"))
|
||||||
@@ -38,12 +38,12 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
const formatedDoc = mergeOneToManyRelation(
|
const formatedDoc = mergeOneToManyRelation(
|
||||||
doc,
|
doc,
|
||||||
"id",
|
"id",
|
||||||
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
({ approverUserId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||||
...el,
|
...el,
|
||||||
envId,
|
envId,
|
||||||
environment: { id: envId, name, slug }
|
environment: { id: envId, name, slug }
|
||||||
}),
|
}),
|
||||||
({ approverId }) => approverId,
|
({ approverUserId }) => approverUserId,
|
||||||
"approvers"
|
"approvers"
|
||||||
);
|
);
|
||||||
return formatedDoc?.[0];
|
return formatedDoc?.[0];
|
||||||
@@ -58,12 +58,12 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
const formatedDoc = mergeOneToManyRelation(
|
const formatedDoc = mergeOneToManyRelation(
|
||||||
docs,
|
docs,
|
||||||
"id",
|
"id",
|
||||||
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
({ approverUserId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||||
...el,
|
...el,
|
||||||
envId,
|
envId,
|
||||||
environment: { id: envId, name, slug }
|
environment: { id: envId, name, slug }
|
||||||
}),
|
}),
|
||||||
({ approverId }) => approverId,
|
({ approverUserId }) => approverUserId,
|
||||||
"approvers"
|
"approvers"
|
||||||
);
|
);
|
||||||
return formatedDoc.map((policy) => ({ ...policy, secretPath: policy.secretPath || undefined }));
|
return formatedDoc.map((policy) => ({ ...policy, secretPath: policy.secretPath || undefined }));
|
||||||
|
@@ -5,7 +5,7 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services
|
|||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
||||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
import { TAccessApprovalPolicyApproverDALFactory } from "./access-approval-policy-approver-dal";
|
import { TAccessApprovalPolicyApproverDALFactory } from "./access-approval-policy-approver-dal";
|
||||||
import { TAccessApprovalPolicyDALFactory } from "./access-approval-policy-dal";
|
import { TAccessApprovalPolicyDALFactory } from "./access-approval-policy-dal";
|
||||||
@@ -24,7 +24,7 @@ type TSecretApprovalPolicyServiceFactoryDep = {
|
|||||||
accessApprovalPolicyDAL: TAccessApprovalPolicyDALFactory;
|
accessApprovalPolicyDAL: TAccessApprovalPolicyDALFactory;
|
||||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findOne">;
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findOne">;
|
||||||
accessApprovalPolicyApproverDAL: TAccessApprovalPolicyApproverDALFactory;
|
accessApprovalPolicyApproverDAL: TAccessApprovalPolicyApproverDALFactory;
|
||||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
|
userDAL: Pick<TUserDALFactory, "findUsersByProjectId">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TAccessApprovalPolicyServiceFactory = ReturnType<typeof accessApprovalPolicyServiceFactory>;
|
export type TAccessApprovalPolicyServiceFactory = ReturnType<typeof accessApprovalPolicyServiceFactory>;
|
||||||
@@ -34,8 +34,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
accessApprovalPolicyApproverDAL,
|
accessApprovalPolicyApproverDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
projectDAL,
|
userDAL,
|
||||||
projectMembershipDAL
|
projectDAL
|
||||||
}: TSecretApprovalPolicyServiceFactoryDep) => {
|
}: TSecretApprovalPolicyServiceFactoryDep) => {
|
||||||
const createAccessApprovalPolicy = async ({
|
const createAccessApprovalPolicy = async ({
|
||||||
name,
|
name,
|
||||||
@@ -69,12 +69,13 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
const env = await projectEnvDAL.findOne({ slug: environment, projectId: project.id });
|
const env = await projectEnvDAL.findOne({ slug: environment, projectId: project.id });
|
||||||
if (!env) throw new BadRequestError({ message: "Environment not found" });
|
if (!env) throw new BadRequestError({ message: "Environment not found" });
|
||||||
|
|
||||||
const secretApprovers = await projectMembershipDAL.find({
|
// We need to get the users by project ID to ensure they are part of the project.
|
||||||
projectId: project.id,
|
const accessApproverUsers = await userDAL.findUsersByProjectId(
|
||||||
$in: { id: approvers }
|
project.id,
|
||||||
});
|
approvers.map((approverUserId) => approverUserId)
|
||||||
|
);
|
||||||
|
|
||||||
if (secretApprovers.length !== approvers.length) {
|
if (accessApproverUsers.length !== approvers.length) {
|
||||||
throw new BadRequestError({ message: "Approver not found in project" });
|
throw new BadRequestError({ message: "Approver not found in project" });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
secretPath,
|
secretPath,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
permissionService,
|
permissionService,
|
||||||
userIds: secretApprovers.map((approver) => approver.userId)
|
userIds: accessApproverUsers.map((user) => user.id)
|
||||||
});
|
});
|
||||||
|
|
||||||
const accessApproval = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
const accessApproval = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
||||||
@@ -99,8 +100,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await accessApprovalPolicyApproverDAL.insertMany(
|
await accessApprovalPolicyApproverDAL.insertMany(
|
||||||
secretApprovers.map(({ id }) => ({
|
accessApproverUsers.map((user) => ({
|
||||||
approverId: id,
|
approverUserId: user.id,
|
||||||
policyId: doc.id
|
policyId: doc.id
|
||||||
})),
|
})),
|
||||||
tx
|
tx
|
||||||
@@ -169,12 +170,9 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
);
|
);
|
||||||
if (approvers) {
|
if (approvers) {
|
||||||
// Find the workspace project memberships of the users passed in the approvers array
|
// Find the workspace project memberships of the users passed in the approvers array
|
||||||
const secretApprovers = await projectMembershipDAL.find(
|
const secretApproverUsers = await userDAL.findUsersByProjectId(
|
||||||
{
|
accessApprovalPolicy.projectId,
|
||||||
projectId: accessApprovalPolicy.projectId,
|
approvers.map((approverUserId) => approverUserId)
|
||||||
$in: { id: approvers }
|
|
||||||
},
|
|
||||||
{ tx }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await verifyApprovers({
|
await verifyApprovers({
|
||||||
@@ -184,15 +182,15 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
secretPath: doc.secretPath!,
|
secretPath: doc.secretPath!,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
permissionService,
|
permissionService,
|
||||||
userIds: secretApprovers.map((approver) => approver.userId)
|
userIds: secretApproverUsers.map((user) => user.id)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (secretApprovers.length !== approvers.length)
|
if (secretApproverUsers.length !== approvers.length)
|
||||||
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
||||||
await accessApprovalPolicyApproverDAL.delete({ policyId: doc.id }, tx);
|
await accessApprovalPolicyApproverDAL.delete({ policyId: doc.id }, tx);
|
||||||
await accessApprovalPolicyApproverDAL.insertMany(
|
await accessApprovalPolicyApproverDAL.insertMany(
|
||||||
secretApprovers.map(({ id }) => ({
|
secretApproverUsers.map((user) => ({
|
||||||
approverId: id,
|
approverUserId: user.id,
|
||||||
policyId: doc.id
|
policyId: doc.id
|
||||||
})),
|
})),
|
||||||
tx
|
tx
|
||||||
|
@@ -11,6 +11,42 @@ export type TAccessApprovalRequestDALFactory = ReturnType<typeof accessApprovalR
|
|||||||
|
|
||||||
export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||||
const accessApprovalRequestOrm = ormify(db, TableName.AccessApprovalRequest);
|
const accessApprovalRequestOrm = ormify(db, TableName.AccessApprovalRequest);
|
||||||
|
const projectUserAdditionalPrivilegeOrm = ormify(db, TableName.ProjectUserAdditionalPrivilege);
|
||||||
|
const groupProjectUserAdditionalPrivilegeOrm = ormify(db, TableName.GroupProjectUserAdditionalPrivilege);
|
||||||
|
|
||||||
|
const deleteMany = async (filter: TFindFilter<TAccessApprovalRequests>, tx?: Knex) => {
|
||||||
|
const transaction = tx || (await db.transaction());
|
||||||
|
|
||||||
|
try {
|
||||||
|
const accessApprovalRequests = await accessApprovalRequestOrm.find(filter, { tx: transaction });
|
||||||
|
|
||||||
|
await projectUserAdditionalPrivilegeOrm.delete(
|
||||||
|
{
|
||||||
|
$in: {
|
||||||
|
id: accessApprovalRequests
|
||||||
|
.filter((req) => Boolean(req.projectUserPrivilegeId))
|
||||||
|
.map((req) => req.projectUserPrivilegeId!)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transaction
|
||||||
|
);
|
||||||
|
|
||||||
|
await groupProjectUserAdditionalPrivilegeOrm.delete(
|
||||||
|
{
|
||||||
|
$in: {
|
||||||
|
id: accessApprovalRequests
|
||||||
|
.filter((req) => Boolean(req.groupProjectUserPrivilegeId))
|
||||||
|
.map((req) => req.groupProjectUserPrivilegeId!)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transaction
|
||||||
|
);
|
||||||
|
|
||||||
|
return await accessApprovalRequestOrm.delete(filter, transaction);
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "DeleteManyAccessApprovalRequest" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const findRequestsWithPrivilegeByPolicyIds = async (policyIds: string[]) => {
|
const findRequestsWithPrivilegeByPolicyIds = async (policyIds: string[]) => {
|
||||||
try {
|
try {
|
||||||
@@ -19,9 +55,14 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.ProjectUserAdditionalPrivilege,
|
TableName.ProjectUserAdditionalPrivilege,
|
||||||
`${TableName.AccessApprovalRequest}.privilegeId`,
|
`${TableName.AccessApprovalRequest}.projectUserPrivilegeId`,
|
||||||
`${TableName.ProjectUserAdditionalPrivilege}.id`
|
`${TableName.ProjectUserAdditionalPrivilege}.id`
|
||||||
)
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.GroupProjectUserAdditionalPrivilege,
|
||||||
|
`${TableName.AccessApprovalRequest}.groupProjectUserPrivilegeId`,
|
||||||
|
`${TableName.GroupProjectUserAdditionalPrivilege}.id`
|
||||||
|
)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.AccessApprovalPolicy,
|
TableName.AccessApprovalPolicy,
|
||||||
`${TableName.AccessApprovalRequest}.policyId`,
|
`${TableName.AccessApprovalRequest}.policyId`,
|
||||||
@@ -50,7 +91,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("envId").withSchema(TableName.AccessApprovalPolicy).as("policyEnvId")
|
db.ref("envId").withSchema(TableName.AccessApprovalPolicy).as("policyEnvId")
|
||||||
)
|
)
|
||||||
|
|
||||||
.select(db.ref("approverId").withSchema(TableName.AccessApprovalPolicyApprover))
|
.select(db.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||||
|
|
||||||
.select(
|
.select(
|
||||||
db.ref("projectId").withSchema(TableName.Environment),
|
db.ref("projectId").withSchema(TableName.Environment),
|
||||||
@@ -59,32 +100,85 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
.select(
|
.select(
|
||||||
db.ref("member").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerMemberId"),
|
db.ref("memberUserId").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerUserId"),
|
||||||
db.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus")
|
db.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Project user additional privilege
|
||||||
.select(
|
.select(
|
||||||
db
|
db
|
||||||
.ref("projectMembershipId")
|
.ref("projectMembershipId")
|
||||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||||
.as("privilegeMembershipId"),
|
.as("projectPrivilegeProjectMembershipId"),
|
||||||
db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeIsTemporary"),
|
|
||||||
db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeTemporaryMode"),
|
db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("projectPrivilegeIsTemporary"),
|
||||||
db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeTemporaryRange"),
|
|
||||||
|
db
|
||||||
|
.ref("temporaryMode")
|
||||||
|
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||||
|
.as("projectPrivilegeTemporaryMode"),
|
||||||
|
|
||||||
|
db
|
||||||
|
.ref("temporaryRange")
|
||||||
|
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||||
|
.as("projectPrivilegeTemporaryRange"),
|
||||||
|
|
||||||
db
|
db
|
||||||
.ref("temporaryAccessStartTime")
|
.ref("temporaryAccessStartTime")
|
||||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||||
.as("privilegeTemporaryAccessStartTime"),
|
.as("projectPrivilegeTemporaryAccessStartTime"),
|
||||||
|
|
||||||
db
|
db
|
||||||
.ref("temporaryAccessEndTime")
|
.ref("temporaryAccessEndTime")
|
||||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||||
.as("privilegeTemporaryAccessEndTime"),
|
.as("projectPrivilegeTemporaryAccessEndTime"),
|
||||||
|
|
||||||
db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegePermissions")
|
db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("projectPrivilegePermissions")
|
||||||
|
)
|
||||||
|
// Group project user additional privilege
|
||||||
|
.select(
|
||||||
|
db
|
||||||
|
.ref("groupProjectMembershipId")
|
||||||
|
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
|
||||||
|
.as("groupPrivilegeGroupProjectMembershipId"),
|
||||||
|
|
||||||
|
db
|
||||||
|
.ref("requestedByUserId")
|
||||||
|
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
|
||||||
|
.as("groupPrivilegeRequestedByUserId"),
|
||||||
|
|
||||||
|
db
|
||||||
|
.ref("isTemporary")
|
||||||
|
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
|
||||||
|
.as("groupPrivilegeIsTemporary"),
|
||||||
|
|
||||||
|
db
|
||||||
|
.ref("temporaryMode")
|
||||||
|
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
|
||||||
|
.as("groupPrivilegeTemporaryMode"),
|
||||||
|
|
||||||
|
db
|
||||||
|
.ref("temporaryRange")
|
||||||
|
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
|
||||||
|
.as("groupPrivilegeTemporaryRange"),
|
||||||
|
|
||||||
|
db
|
||||||
|
.ref("temporaryAccessStartTime")
|
||||||
|
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
|
||||||
|
.as("groupPrivilegeTemporaryAccessStartTime"),
|
||||||
|
|
||||||
|
db
|
||||||
|
.ref("temporaryAccessEndTime")
|
||||||
|
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
|
||||||
|
.as("groupPrivilegeTemporaryAccessEndTime"),
|
||||||
|
db
|
||||||
|
.ref("permissions")
|
||||||
|
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
|
||||||
|
.as("groupPrivilegePermissions")
|
||||||
)
|
)
|
||||||
.orderBy(`${TableName.AccessApprovalRequest}.createdAt`, "desc");
|
.orderBy(`${TableName.AccessApprovalRequest}.createdAt`, "desc");
|
||||||
|
|
||||||
const formattedDocs = sqlNestRelationships({
|
const projectUserFormattedDocs = sqlNestRelationships({
|
||||||
data: docs,
|
data: docs,
|
||||||
key: "id",
|
key: "id",
|
||||||
parentMapper: (doc) => ({
|
parentMapper: (doc) => ({
|
||||||
@@ -99,33 +193,49 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
secretPath: doc.policySecretPath,
|
secretPath: doc.policySecretPath,
|
||||||
envId: doc.policyEnvId
|
envId: doc.policyEnvId
|
||||||
},
|
},
|
||||||
privilege: doc.privilegeId
|
// eslint-disable-next-line no-nested-ternary
|
||||||
|
privilege: doc.projectUserPrivilegeId
|
||||||
? {
|
? {
|
||||||
membershipId: doc.privilegeMembershipId,
|
projectMembershipId: doc.projectMembershipId,
|
||||||
isTemporary: doc.privilegeIsTemporary,
|
groupMembershipId: null,
|
||||||
temporaryMode: doc.privilegeTemporaryMode,
|
requestedByUserId: null,
|
||||||
temporaryRange: doc.privilegeTemporaryRange,
|
isTemporary: doc.projectPrivilegeIsTemporary,
|
||||||
temporaryAccessStartTime: doc.privilegeTemporaryAccessStartTime,
|
temporaryMode: doc.projectPrivilegeTemporaryMode,
|
||||||
temporaryAccessEndTime: doc.privilegeTemporaryAccessEndTime,
|
temporaryRange: doc.projectPrivilegeTemporaryRange,
|
||||||
permissions: doc.privilegePermissions
|
temporaryAccessStartTime: doc.projectPrivilegeTemporaryAccessStartTime,
|
||||||
|
temporaryAccessEndTime: doc.projectPrivilegeTemporaryAccessEndTime,
|
||||||
|
permissions: doc.projectPrivilegePermissions
|
||||||
}
|
}
|
||||||
: null,
|
: doc.groupProjectUserPrivilegeId
|
||||||
|
? {
|
||||||
|
groupMembershipId: doc.groupPrivilegeGroupProjectMembershipId,
|
||||||
|
requestedByUserId: doc.groupPrivilegeRequestedByUserId,
|
||||||
|
projectMembershipId: null,
|
||||||
|
isTemporary: doc.groupPrivilegeIsTemporary,
|
||||||
|
temporaryMode: doc.groupPrivilegeTemporaryMode,
|
||||||
|
temporaryRange: doc.groupPrivilegeTemporaryRange,
|
||||||
|
temporaryAccessStartTime: doc.groupPrivilegeTemporaryAccessStartTime,
|
||||||
|
temporaryAccessEndTime: doc.groupPrivilegeTemporaryAccessEndTime,
|
||||||
|
permissions: doc.groupPrivilegePermissions
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
|
||||||
isApproved: !!doc.privilegeId
|
isApproved: Boolean(doc.projectUserPrivilegeId || doc.groupProjectUserPrivilegeId)
|
||||||
}),
|
}),
|
||||||
childrenMapper: [
|
childrenMapper: [
|
||||||
{
|
{
|
||||||
key: "reviewerMemberId",
|
key: "reviewerUserId",
|
||||||
label: "reviewers" as const,
|
label: "reviewers" as const,
|
||||||
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
|
mapper: ({ reviewerUserId, reviewerStatus: status }) =>
|
||||||
|
reviewerUserId ? { member: reviewerUserId, status } : undefined
|
||||||
},
|
},
|
||||||
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
|
{ key: "approverUserId", label: "approvers" as const, mapper: ({ approverUserId }) => approverUserId }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!formattedDocs) return [];
|
if (!projectUserFormattedDocs) return [];
|
||||||
|
|
||||||
return formattedDocs.map((doc) => ({
|
return projectUserFormattedDocs.map((doc) => ({
|
||||||
...doc,
|
...doc,
|
||||||
policy: { ...doc.policy, approvers: doc.approvers }
|
policy: { ...doc.policy, approvers: doc.approvers }
|
||||||
}));
|
}));
|
||||||
@@ -157,7 +267,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
||||||
.select(selectAllTableCols(TableName.AccessApprovalRequest))
|
.select(selectAllTableCols(TableName.AccessApprovalRequest))
|
||||||
.select(
|
.select(
|
||||||
tx.ref("member").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerMemberId"),
|
tx.ref("memberUserId").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerUserId"),
|
||||||
tx.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus"),
|
tx.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus"),
|
||||||
tx.ref("id").withSchema(TableName.AccessApprovalPolicy).as("policyId"),
|
tx.ref("id").withSchema(TableName.AccessApprovalPolicy).as("policyId"),
|
||||||
tx.ref("name").withSchema(TableName.AccessApprovalPolicy).as("policyName"),
|
tx.ref("name").withSchema(TableName.AccessApprovalPolicy).as("policyName"),
|
||||||
@@ -165,7 +275,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
|
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
|
||||||
tx.ref("secretPath").withSchema(TableName.AccessApprovalPolicy).as("policySecretPath"),
|
tx.ref("secretPath").withSchema(TableName.AccessApprovalPolicy).as("policySecretPath"),
|
||||||
tx.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals"),
|
tx.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals"),
|
||||||
tx.ref("approverId").withSchema(TableName.AccessApprovalPolicyApprover)
|
tx.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover)
|
||||||
);
|
);
|
||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
@@ -188,11 +298,12 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
}),
|
}),
|
||||||
childrenMapper: [
|
childrenMapper: [
|
||||||
{
|
{
|
||||||
key: "reviewerMemberId",
|
key: "reviewerUserId",
|
||||||
label: "reviewers" as const,
|
label: "reviewers" as const,
|
||||||
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
|
mapper: ({ reviewerUserId, reviewerStatus: status }) =>
|
||||||
|
reviewerUserId ? { member: reviewerUserId, status } : undefined
|
||||||
},
|
},
|
||||||
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
|
{ key: "approverUserId", label: "approvers" as const, mapper: ({ approverUserId }) => approverUserId }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
if (!formatedDoc?.[0]) return;
|
if (!formatedDoc?.[0]) return;
|
||||||
@@ -214,12 +325,6 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.AccessApprovalPolicy}.id`
|
`${TableName.AccessApprovalPolicy}.id`
|
||||||
)
|
)
|
||||||
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
||||||
.leftJoin(
|
|
||||||
TableName.ProjectUserAdditionalPrivilege,
|
|
||||||
`${TableName.AccessApprovalRequest}.privilegeId`,
|
|
||||||
`${TableName.ProjectUserAdditionalPrivilege}.id`
|
|
||||||
)
|
|
||||||
|
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.AccessApprovalRequestReviewer,
|
TableName.AccessApprovalRequestReviewer,
|
||||||
`${TableName.AccessApprovalRequest}.id`,
|
`${TableName.AccessApprovalRequest}.id`,
|
||||||
@@ -229,7 +334,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
.where(`${TableName.Environment}.projectId`, projectId)
|
.where(`${TableName.Environment}.projectId`, projectId)
|
||||||
.select(selectAllTableCols(TableName.AccessApprovalRequest))
|
.select(selectAllTableCols(TableName.AccessApprovalRequest))
|
||||||
.select(db.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus"))
|
.select(db.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus"))
|
||||||
.select(db.ref("member").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerMemberId"));
|
.select(db.ref("memberUserId").withSchema(TableName.AccessApprovalRequestReviewer).as("memberUserId"));
|
||||||
|
|
||||||
const formattedRequests = sqlNestRelationships({
|
const formattedRequests = sqlNestRelationships({
|
||||||
data: accessRequests,
|
data: accessRequests,
|
||||||
@@ -239,21 +344,28 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
}),
|
}),
|
||||||
childrenMapper: [
|
childrenMapper: [
|
||||||
{
|
{
|
||||||
key: "reviewerMemberId",
|
key: "memberUserId",
|
||||||
label: "reviewers" as const,
|
label: "reviewers" as const,
|
||||||
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
|
mapper: ({ memberUserId, reviewerStatus: status }) =>
|
||||||
|
memberUserId ? { member: memberUserId, status } : undefined
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
// an approval is pending if there is no reviewer rejections and no privilege ID is set
|
// an approval is pending if there is no reviewer rejections and no privilege ID is set
|
||||||
const pendingApprovals = formattedRequests.filter(
|
const pendingApprovals = formattedRequests.filter(
|
||||||
(req) => !req.privilegeId && !req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED)
|
(req) =>
|
||||||
|
!req.projectUserPrivilegeId &&
|
||||||
|
!req.groupProjectUserPrivilegeId &&
|
||||||
|
!req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED)
|
||||||
);
|
);
|
||||||
|
|
||||||
// an approval is finalized if there are any rejections or a privilege ID is set
|
// an approval is finalized if there are any rejections or a privilege ID is set
|
||||||
const finalizedApprovals = formattedRequests.filter(
|
const finalizedApprovals = formattedRequests.filter(
|
||||||
(req) => req.privilegeId || req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED)
|
(req) =>
|
||||||
|
req.projectUserPrivilegeId ||
|
||||||
|
req.groupProjectUserPrivilegeId ||
|
||||||
|
req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED)
|
||||||
);
|
);
|
||||||
|
|
||||||
return { pendingCount: pendingApprovals.length, finalizedCount: finalizedApprovals.length };
|
return { pendingCount: pendingApprovals.length, finalizedCount: finalizedApprovals.length };
|
||||||
@@ -262,5 +374,5 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { ...accessApprovalRequestOrm, findById, findRequestsWithPrivilegeByPolicyIds, getCount };
|
return { ...accessApprovalRequestOrm, findById, findRequestsWithPrivilegeByPolicyIds, getCount, delete: deleteMany };
|
||||||
};
|
};
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
|
|
||||||
import { ProjectMembershipRole } from "@app/db/schemas";
|
import { ProjectMembershipRole, TProjectUserAdditionalPrivilege } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
@@ -14,7 +15,9 @@ import { TUserDALFactory } from "@app/services/user/user-dal";
|
|||||||
import { TAccessApprovalPolicyApproverDALFactory } from "../access-approval-policy/access-approval-policy-approver-dal";
|
import { TAccessApprovalPolicyApproverDALFactory } from "../access-approval-policy/access-approval-policy-approver-dal";
|
||||||
import { TAccessApprovalPolicyDALFactory } from "../access-approval-policy/access-approval-policy-dal";
|
import { TAccessApprovalPolicyDALFactory } from "../access-approval-policy/access-approval-policy-dal";
|
||||||
import { verifyApprovers } from "../access-approval-policy/access-approval-policy-fns";
|
import { verifyApprovers } from "../access-approval-policy/access-approval-policy-fns";
|
||||||
|
import { TGroupProjectUserAdditionalPrivilegeDALFactory } from "../group-project-user-additional-privilege/group-project-user-additional-privilege-dal";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
|
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||||
import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal";
|
import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal";
|
||||||
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "../project-user-additional-privilege/project-user-additional-privilege-types";
|
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "../project-user-additional-privilege/project-user-additional-privilege-types";
|
||||||
import { TAccessApprovalRequestDALFactory } from "./access-approval-request-dal";
|
import { TAccessApprovalRequestDALFactory } from "./access-approval-request-dal";
|
||||||
@@ -23,13 +26,15 @@ import { TAccessApprovalRequestReviewerDALFactory } from "./access-approval-requ
|
|||||||
import {
|
import {
|
||||||
ApprovalStatus,
|
ApprovalStatus,
|
||||||
TCreateAccessApprovalRequestDTO,
|
TCreateAccessApprovalRequestDTO,
|
||||||
|
TDeleteApprovalRequestDTO,
|
||||||
TGetAccessRequestCountDTO,
|
TGetAccessRequestCountDTO,
|
||||||
TListApprovalRequestsDTO,
|
TListApprovalRequestsDTO,
|
||||||
TReviewAccessRequestDTO
|
TReviewAccessRequestDTO
|
||||||
} from "./access-approval-request-types";
|
} from "./access-approval-request-types";
|
||||||
|
|
||||||
type TSecretApprovalRequestServiceFactoryDep = {
|
type TAccessApprovalRequestServiceFactoryDep = {
|
||||||
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "create" | "findById">;
|
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "create" | "findById" | "deleteById">;
|
||||||
|
groupAdditionalPrivilegeDAL: TGroupProjectUserAdditionalPrivilegeDALFactory;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||||
accessApprovalPolicyApproverDAL: Pick<TAccessApprovalPolicyApproverDALFactory, "find">;
|
accessApprovalPolicyApproverDAL: Pick<TAccessApprovalPolicyApproverDALFactory, "find">;
|
||||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
||||||
@@ -44,6 +49,7 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
|||||||
| "updateById"
|
| "updateById"
|
||||||
| "findOne"
|
| "findOne"
|
||||||
| "getCount"
|
| "getCount"
|
||||||
|
| "deleteById"
|
||||||
>;
|
>;
|
||||||
accessApprovalPolicyDAL: Pick<TAccessApprovalPolicyDALFactory, "findOne" | "find">;
|
accessApprovalPolicyDAL: Pick<TAccessApprovalPolicyDALFactory, "findOne" | "find">;
|
||||||
accessApprovalRequestReviewerDAL: Pick<
|
accessApprovalRequestReviewerDAL: Pick<
|
||||||
@@ -52,7 +58,10 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
|||||||
>;
|
>;
|
||||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findById">;
|
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findById">;
|
||||||
smtpService: Pick<TSmtpService, "sendMail">;
|
smtpService: Pick<TSmtpService, "sendMail">;
|
||||||
userDAL: Pick<TUserDALFactory, "findUserByProjectMembershipId" | "findUsersByProjectMembershipIds">;
|
userDAL: Pick<
|
||||||
|
TUserDALFactory,
|
||||||
|
"findUserByProjectMembershipId" | "findUsersByProjectMembershipIds" | "findUsersByProjectId" | "findUserByProjectId"
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TAccessApprovalRequestServiceFactory = ReturnType<typeof accessApprovalRequestServiceFactory>;
|
export type TAccessApprovalRequestServiceFactory = ReturnType<typeof accessApprovalRequestServiceFactory>;
|
||||||
@@ -62,6 +71,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
accessApprovalRequestDAL,
|
accessApprovalRequestDAL,
|
||||||
|
groupAdditionalPrivilegeDAL,
|
||||||
accessApprovalRequestReviewerDAL,
|
accessApprovalRequestReviewerDAL,
|
||||||
projectMembershipDAL,
|
projectMembershipDAL,
|
||||||
accessApprovalPolicyDAL,
|
accessApprovalPolicyDAL,
|
||||||
@@ -69,7 +79,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
additionalPrivilegeDAL,
|
additionalPrivilegeDAL,
|
||||||
smtpService,
|
smtpService,
|
||||||
userDAL
|
userDAL
|
||||||
}: TSecretApprovalRequestServiceFactoryDep) => {
|
}: TAccessApprovalRequestServiceFactoryDep) => {
|
||||||
const createAccessApprovalRequest = async ({
|
const createAccessApprovalRequest = async ({
|
||||||
isTemporary,
|
isTemporary,
|
||||||
temporaryRange,
|
temporaryRange,
|
||||||
@@ -94,9 +104,6 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
);
|
);
|
||||||
if (!membership) throw new UnauthorizedError({ message: "You are not a member of this project" });
|
if (!membership) throw new UnauthorizedError({ message: "You are not a member of this project" });
|
||||||
|
|
||||||
const requestedByUser = await userDAL.findUserByProjectMembershipId(membership.id);
|
|
||||||
if (!requestedByUser) throw new UnauthorizedError({ message: "User not found" });
|
|
||||||
|
|
||||||
await projectDAL.checkProjectUpgradeStatus(project.id);
|
await projectDAL.checkProjectUpgradeStatus(project.id);
|
||||||
|
|
||||||
const { envSlug, secretPath, accessTypes } = verifyRequestedPermissions({ permissions: requestedPermissions });
|
const { envSlug, secretPath, accessTypes } = verifyRequestedPermissions({ permissions: requestedPermissions });
|
||||||
@@ -114,25 +121,43 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
policyId: policy.id
|
policyId: policy.id
|
||||||
});
|
});
|
||||||
|
|
||||||
const approverUsers = await userDAL.findUsersByProjectMembershipIds(
|
if (approvers.some((approver) => !approver.approverUserId)) {
|
||||||
approvers.map((approver) => approver.approverId)
|
throw new BadRequestError({ message: "Policy approvers must be assigned to users" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const approverUsers = await userDAL.findUsersByProjectId(
|
||||||
|
project.id,
|
||||||
|
approvers.map((approver) => approver.approverUserId!)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const requestedByUser = await userDAL.findUserByProjectId(project.id, actorId);
|
||||||
|
|
||||||
|
if (!requestedByUser) throw new BadRequestError({ message: "User not found in project" });
|
||||||
|
|
||||||
const duplicateRequests = await accessApprovalRequestDAL.find({
|
const duplicateRequests = await accessApprovalRequestDAL.find({
|
||||||
policyId: policy.id,
|
policyId: policy.id,
|
||||||
requestedBy: membership.id,
|
requestedByUserId: actorId,
|
||||||
permissions: JSON.stringify(requestedPermissions),
|
permissions: JSON.stringify(requestedPermissions),
|
||||||
isTemporary
|
isTemporary
|
||||||
});
|
});
|
||||||
|
|
||||||
if (duplicateRequests?.length > 0) {
|
if (duplicateRequests?.length > 0) {
|
||||||
for await (const duplicateRequest of duplicateRequests) {
|
for await (const duplicateRequest of duplicateRequests) {
|
||||||
if (duplicateRequest.privilegeId) {
|
let foundPrivilege: Pick<
|
||||||
const privilege = await additionalPrivilegeDAL.findById(duplicateRequest.privilegeId);
|
TProjectUserAdditionalPrivilege,
|
||||||
|
"temporaryAccessEndTime" | "isTemporary" | "id"
|
||||||
|
> | null = null;
|
||||||
|
|
||||||
const isExpired = new Date() > new Date(privilege.temporaryAccessEndTime || ("" as string));
|
if (duplicateRequest.projectUserPrivilegeId) {
|
||||||
|
foundPrivilege = await additionalPrivilegeDAL.findById(duplicateRequest.projectUserPrivilegeId);
|
||||||
|
} else if (duplicateRequest.groupProjectUserPrivilegeId) {
|
||||||
|
foundPrivilege = await groupAdditionalPrivilegeDAL.findById(duplicateRequest.groupProjectUserPrivilegeId);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isExpired || !privilege.isTemporary) {
|
if (foundPrivilege) {
|
||||||
|
const isExpired = new Date() > new Date(foundPrivilege.temporaryAccessEndTime || ("" as string));
|
||||||
|
|
||||||
|
if (!isExpired || !foundPrivilege.isTemporary) {
|
||||||
throw new BadRequestError({ message: "You already have an active privilege with the same criteria" });
|
throw new BadRequestError({ message: "You already have an active privilege with the same criteria" });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -150,10 +175,18 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const approval = await accessApprovalRequestDAL.transaction(async (tx) => {
|
const approval = await accessApprovalRequestDAL.transaction(async (tx) => {
|
||||||
|
const requesterUser = await userDAL.findUserByProjectId(project.id, actorId);
|
||||||
|
|
||||||
|
if (!requesterUser?.projectMembershipId && !requesterUser?.groupProjectMembershipId) {
|
||||||
|
throw new BadRequestError({ message: "You don't have a membership for this project" });
|
||||||
|
}
|
||||||
|
|
||||||
const approvalRequest = await accessApprovalRequestDAL.create(
|
const approvalRequest = await accessApprovalRequestDAL.create(
|
||||||
{
|
{
|
||||||
|
projectMembershipId: requesterUser.projectMembershipId || null,
|
||||||
|
groupMembershipId: requesterUser.groupProjectMembershipId || null,
|
||||||
policyId: policy.id,
|
policyId: policy.id,
|
||||||
requestedBy: membership.id,
|
requestedByUserId: actorId, // This is the user ID of the person who made the request
|
||||||
temporaryRange: temporaryRange || null,
|
temporaryRange: temporaryRange || null,
|
||||||
permissions: JSON.stringify(requestedPermissions),
|
permissions: JSON.stringify(requestedPermissions),
|
||||||
isTemporary
|
isTemporary
|
||||||
@@ -187,9 +220,62 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
return { request: approval };
|
return { request: approval };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteAccessApprovalRequest = async ({
|
||||||
|
projectSlug,
|
||||||
|
actor,
|
||||||
|
requestId,
|
||||||
|
actorOrgId,
|
||||||
|
actorId,
|
||||||
|
actorAuthMethod
|
||||||
|
}: TDeleteApprovalRequestDTO) => {
|
||||||
|
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||||
|
if (!project) throw new UnauthorizedError({ message: "Project not found" });
|
||||||
|
|
||||||
|
const { membership, permission } = await permissionService.getProjectPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
project.id,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
if (!membership) throw new UnauthorizedError({ message: "You are not a member of this project" });
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
ProjectPermissionSub.SecretApproval
|
||||||
|
);
|
||||||
|
|
||||||
|
const accessApprovalRequest = await accessApprovalRequestDAL.findById(requestId);
|
||||||
|
|
||||||
|
if (!accessApprovalRequest?.projectUserPrivilegeId && !accessApprovalRequest?.groupProjectUserPrivilegeId) {
|
||||||
|
throw new BadRequestError({ message: "Access request must be approved to be deleted" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accessApprovalRequest?.projectId !== project.id) {
|
||||||
|
throw new UnauthorizedError({ message: "Request not found in project" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const approvers = await accessApprovalPolicyApproverDAL.find({
|
||||||
|
policyId: accessApprovalRequest.policyId
|
||||||
|
});
|
||||||
|
|
||||||
|
// make sure the actor (actorId) is an approver
|
||||||
|
if (!approvers.some((approver) => approver.approverUserId === actorId)) {
|
||||||
|
throw new UnauthorizedError({ message: "Only policy approvers can delete access requests" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accessApprovalRequest.projectUserPrivilegeId) {
|
||||||
|
await additionalPrivilegeDAL.deleteById(accessApprovalRequest.projectUserPrivilegeId);
|
||||||
|
} else if (accessApprovalRequest.groupProjectUserPrivilegeId) {
|
||||||
|
await groupAdditionalPrivilegeDAL.deleteById(accessApprovalRequest.groupProjectUserPrivilegeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { request: accessApprovalRequest };
|
||||||
|
};
|
||||||
|
|
||||||
const listApprovalRequests = async ({
|
const listApprovalRequests = async ({
|
||||||
projectSlug,
|
projectSlug,
|
||||||
authorProjectMembershipId,
|
authorUserId,
|
||||||
envSlug,
|
envSlug,
|
||||||
actor,
|
actor,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
@@ -211,13 +297,8 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
const policies = await accessApprovalPolicyDAL.find({ projectId: project.id });
|
const policies = await accessApprovalPolicyDAL.find({ projectId: project.id });
|
||||||
let requests = await accessApprovalRequestDAL.findRequestsWithPrivilegeByPolicyIds(policies.map((p) => p.id));
|
let requests = await accessApprovalRequestDAL.findRequestsWithPrivilegeByPolicyIds(policies.map((p) => p.id));
|
||||||
|
|
||||||
if (authorProjectMembershipId) {
|
if (authorUserId) requests = requests.filter((request) => request.requestedByUserId === authorUserId);
|
||||||
requests = requests.filter((request) => request.requestedBy === authorProjectMembershipId);
|
if (envSlug) requests = requests.filter((request) => request.environment === envSlug);
|
||||||
}
|
|
||||||
|
|
||||||
if (envSlug) {
|
|
||||||
requests = requests.filter((request) => request.environment === envSlug);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { requests };
|
return { requests };
|
||||||
};
|
};
|
||||||
@@ -246,8 +327,8 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
accessApprovalRequest.requestedBy !== membership.id && // The request wasn't made by the current user
|
accessApprovalRequest.requestedByUserId !== actorId && // The request wasn't made by the current user
|
||||||
!policy.approvers.find((approverId) => approverId === membership.id) // The request isn't performed by an assigned approver
|
!policy.approvers.find((approverUserId) => approverUserId === membership.id) // The request isn't performed by an assigned approver
|
||||||
) {
|
) {
|
||||||
throw new UnauthorizedError({ message: "You are not authorized to approve this request" });
|
throw new UnauthorizedError({ message: "You are not authorized to approve this request" });
|
||||||
}
|
}
|
||||||
@@ -273,7 +354,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
const review = await accessApprovalRequestReviewerDAL.findOne(
|
const review = await accessApprovalRequestReviewerDAL.findOne(
|
||||||
{
|
{
|
||||||
requestId: accessApprovalRequest.id,
|
requestId: accessApprovalRequest.id,
|
||||||
member: membership.id
|
memberUserId: actorId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -282,7 +363,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
{
|
{
|
||||||
status,
|
status,
|
||||||
requestId: accessApprovalRequest.id,
|
requestId: accessApprovalRequest.id,
|
||||||
member: membership.id
|
memberUserId: actorId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -297,41 +378,92 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
throw new BadRequestError({ message: "Temporary range is required for temporary access" });
|
throw new BadRequestError({ message: "Temporary range is required for temporary access" });
|
||||||
}
|
}
|
||||||
|
|
||||||
let privilegeId: string | null = null;
|
let projectUserPrivilegeId: string | null = null;
|
||||||
|
let groupProjectMembershipId: string | null = null;
|
||||||
|
|
||||||
|
if (!accessApprovalRequest.groupMembershipId && !accessApprovalRequest.projectMembershipId) {
|
||||||
|
throw new BadRequestError({ message: "Project membership or group membership is required" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permanent access
|
||||||
if (!accessApprovalRequest.isTemporary && !accessApprovalRequest.temporaryRange) {
|
if (!accessApprovalRequest.isTemporary && !accessApprovalRequest.temporaryRange) {
|
||||||
// Permanent access
|
if (accessApprovalRequest.groupMembershipId) {
|
||||||
const privilege = await additionalPrivilegeDAL.create(
|
// Group user privilege
|
||||||
{
|
const groupProjectUserAdditionalPrivilege = await groupAdditionalPrivilegeDAL.create(
|
||||||
projectMembershipId: accessApprovalRequest.requestedBy,
|
{
|
||||||
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
groupProjectMembershipId: accessApprovalRequest.groupMembershipId,
|
||||||
permissions: JSON.stringify(accessApprovalRequest.permissions)
|
requestedByUserId: accessApprovalRequest.requestedByUserId,
|
||||||
},
|
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
||||||
tx
|
permissions: JSON.stringify(accessApprovalRequest.permissions)
|
||||||
);
|
},
|
||||||
privilegeId = privilege.id;
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
groupProjectMembershipId = groupProjectUserAdditionalPrivilege.id;
|
||||||
|
} else {
|
||||||
|
// Project user privilege
|
||||||
|
const privilege = await additionalPrivilegeDAL.create(
|
||||||
|
{
|
||||||
|
projectMembershipId: accessApprovalRequest.projectMembershipId!,
|
||||||
|
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
||||||
|
permissions: JSON.stringify(accessApprovalRequest.permissions)
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
projectUserPrivilegeId = privilege.id;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Temporary access
|
// Temporary access
|
||||||
const relativeTempAllocatedTimeInMs = ms(accessApprovalRequest.temporaryRange!);
|
const relativeTempAllocatedTimeInMs = ms(accessApprovalRequest.temporaryRange!);
|
||||||
const startTime = new Date();
|
const startTime = new Date();
|
||||||
|
|
||||||
const privilege = await additionalPrivilegeDAL.create(
|
if (accessApprovalRequest.groupMembershipId) {
|
||||||
{
|
// Group user privilege
|
||||||
projectMembershipId: accessApprovalRequest.requestedBy,
|
const groupProjectUserAdditionalPrivilege = await groupAdditionalPrivilegeDAL.create(
|
||||||
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
{
|
||||||
permissions: JSON.stringify(accessApprovalRequest.permissions),
|
groupProjectMembershipId: accessApprovalRequest.groupMembershipId,
|
||||||
isTemporary: true,
|
requestedByUserId: accessApprovalRequest.requestedByUserId,
|
||||||
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
|
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
||||||
temporaryRange: accessApprovalRequest.temporaryRange!,
|
permissions: JSON.stringify(accessApprovalRequest.permissions),
|
||||||
temporaryAccessStartTime: startTime,
|
isTemporary: true,
|
||||||
temporaryAccessEndTime: new Date(new Date(startTime).getTime() + relativeTempAllocatedTimeInMs)
|
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
|
||||||
},
|
temporaryRange: accessApprovalRequest.temporaryRange!,
|
||||||
tx
|
temporaryAccessStartTime: startTime,
|
||||||
);
|
temporaryAccessEndTime: new Date(new Date(startTime).getTime() + relativeTempAllocatedTimeInMs)
|
||||||
privilegeId = privilege.id;
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
groupProjectMembershipId = groupProjectUserAdditionalPrivilege.id;
|
||||||
|
} else {
|
||||||
|
const privilege = await additionalPrivilegeDAL.create(
|
||||||
|
{
|
||||||
|
projectMembershipId: accessApprovalRequest.projectMembershipId!,
|
||||||
|
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
||||||
|
permissions: JSON.stringify(accessApprovalRequest.permissions),
|
||||||
|
isTemporary: true,
|
||||||
|
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
|
||||||
|
temporaryRange: accessApprovalRequest.temporaryRange!,
|
||||||
|
temporaryAccessStartTime: startTime,
|
||||||
|
temporaryAccessEndTime: new Date(new Date(startTime).getTime() + relativeTempAllocatedTimeInMs)
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
projectUserPrivilegeId = privilege.id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await accessApprovalRequestDAL.updateById(accessApprovalRequest.id, { privilegeId }, tx);
|
if (projectUserPrivilegeId) {
|
||||||
|
await accessApprovalRequestDAL.updateById(accessApprovalRequest.id, { projectUserPrivilegeId }, tx);
|
||||||
|
} else if (groupProjectMembershipId) {
|
||||||
|
await accessApprovalRequestDAL.updateById(
|
||||||
|
accessApprovalRequest.id,
|
||||||
|
{ groupProjectUserPrivilegeId: groupProjectMembershipId },
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new BadRequestError({ message: "No privilege was created" });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newReview;
|
return newReview;
|
||||||
@@ -364,6 +496,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
createAccessApprovalRequest,
|
createAccessApprovalRequest,
|
||||||
listApprovalRequests,
|
listApprovalRequests,
|
||||||
reviewAccessRequest,
|
reviewAccessRequest,
|
||||||
|
deleteAccessApprovalRequest,
|
||||||
getCount
|
getCount
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -28,6 +28,11 @@ export type TCreateAccessApprovalRequestDTO = {
|
|||||||
|
|
||||||
export type TListApprovalRequestsDTO = {
|
export type TListApprovalRequestsDTO = {
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
authorProjectMembershipId?: string;
|
authorUserId?: string;
|
||||||
envSlug?: string;
|
envSlug?: string;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TDeleteApprovalRequestDTO = {
|
||||||
|
requestId: string;
|
||||||
|
projectSlug: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
@@ -3,6 +3,7 @@ import { RawAxiosRequestHeaders } from "axios";
|
|||||||
import { SecretKeyEncoding } from "@app/db/schemas";
|
import { SecretKeyEncoding } from "@app/db/schemas";
|
||||||
import { request } from "@app/lib/config/request";
|
import { request } from "@app/lib/config/request";
|
||||||
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
||||||
|
import { logger } from "@app/lib/logger";
|
||||||
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
@@ -112,7 +113,35 @@ export const auditLogQueueServiceFactory = ({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
queueService.start(QueueName.AuditLogPrune, async () => {
|
||||||
|
logger.info(`${QueueName.AuditLogPrune}: queue task started`);
|
||||||
|
await auditLogDAL.pruneAuditLog();
|
||||||
|
logger.info(`${QueueName.AuditLogPrune}: queue task completed`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// we do a repeat cron job in utc timezone at 12 Midnight each day
|
||||||
|
const startAuditLogPruneJob = async () => {
|
||||||
|
// clear previous job
|
||||||
|
await queueService.stopRepeatableJob(
|
||||||
|
QueueName.AuditLogPrune,
|
||||||
|
QueueJobs.AuditLogPrune,
|
||||||
|
{ pattern: "0 0 * * *", utc: true },
|
||||||
|
QueueName.AuditLogPrune // just a job id
|
||||||
|
);
|
||||||
|
|
||||||
|
await queueService.queue(QueueName.AuditLogPrune, QueueJobs.AuditLogPrune, undefined, {
|
||||||
|
delay: 5000,
|
||||||
|
jobId: QueueName.AuditLogPrune,
|
||||||
|
repeat: { pattern: "0 0 * * *", utc: true }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
queueService.listen(QueueName.AuditLogPrune, "failed", (err) => {
|
||||||
|
logger.error(err?.failedReason, `${QueueName.AuditLogPrune}: log pruning failed`);
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pushToLog
|
pushToLog,
|
||||||
|
startAuditLogPruneJob
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -51,7 +51,6 @@ export enum EventType {
|
|||||||
UNAUTHORIZE_INTEGRATION = "unauthorize-integration",
|
UNAUTHORIZE_INTEGRATION = "unauthorize-integration",
|
||||||
CREATE_INTEGRATION = "create-integration",
|
CREATE_INTEGRATION = "create-integration",
|
||||||
DELETE_INTEGRATION = "delete-integration",
|
DELETE_INTEGRATION = "delete-integration",
|
||||||
MANUAL_SYNC_INTEGRATION = "manual-sync-integration",
|
|
||||||
ADD_TRUSTED_IP = "add-trusted-ip",
|
ADD_TRUSTED_IP = "add-trusted-ip",
|
||||||
UPDATE_TRUSTED_IP = "update-trusted-ip",
|
UPDATE_TRUSTED_IP = "update-trusted-ip",
|
||||||
DELETE_TRUSTED_IP = "delete-trusted-ip",
|
DELETE_TRUSTED_IP = "delete-trusted-ip",
|
||||||
@@ -64,25 +63,9 @@ export enum EventType {
|
|||||||
ADD_IDENTITY_UNIVERSAL_AUTH = "add-identity-universal-auth",
|
ADD_IDENTITY_UNIVERSAL_AUTH = "add-identity-universal-auth",
|
||||||
UPDATE_IDENTITY_UNIVERSAL_AUTH = "update-identity-universal-auth",
|
UPDATE_IDENTITY_UNIVERSAL_AUTH = "update-identity-universal-auth",
|
||||||
GET_IDENTITY_UNIVERSAL_AUTH = "get-identity-universal-auth",
|
GET_IDENTITY_UNIVERSAL_AUTH = "get-identity-universal-auth",
|
||||||
LOGIN_IDENTITY_KUBERNETES_AUTH = "login-identity-kubernetes-auth",
|
|
||||||
ADD_IDENTITY_KUBERNETES_AUTH = "add-identity-kubernetes-auth",
|
|
||||||
UPDATE_IDENTITY_KUBENETES_AUTH = "update-identity-kubernetes-auth",
|
|
||||||
GET_IDENTITY_KUBERNETES_AUTH = "get-identity-kubernetes-auth",
|
|
||||||
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
|
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
|
||||||
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
|
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
|
||||||
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
|
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
|
||||||
LOGIN_IDENTITY_GCP_AUTH = "login-identity-gcp-auth",
|
|
||||||
ADD_IDENTITY_GCP_AUTH = "add-identity-gcp-auth",
|
|
||||||
UPDATE_IDENTITY_GCP_AUTH = "update-identity-gcp-auth",
|
|
||||||
GET_IDENTITY_GCP_AUTH = "get-identity-gcp-auth",
|
|
||||||
LOGIN_IDENTITY_AWS_AUTH = "login-identity-aws-auth",
|
|
||||||
ADD_IDENTITY_AWS_AUTH = "add-identity-aws-auth",
|
|
||||||
UPDATE_IDENTITY_AWS_AUTH = "update-identity-aws-auth",
|
|
||||||
GET_IDENTITY_AWS_AUTH = "get-identity-aws-auth",
|
|
||||||
LOGIN_IDENTITY_AZURE_AUTH = "login-identity-azure-auth",
|
|
||||||
ADD_IDENTITY_AZURE_AUTH = "add-identity-azure-auth",
|
|
||||||
UPDATE_IDENTITY_AZURE_AUTH = "update-identity-azure-auth",
|
|
||||||
GET_IDENTITY_AZURE_AUTH = "get-identity-azure-auth",
|
|
||||||
CREATE_ENVIRONMENT = "create-environment",
|
CREATE_ENVIRONMENT = "create-environment",
|
||||||
UPDATE_ENVIRONMENT = "update-environment",
|
UPDATE_ENVIRONMENT = "update-environment",
|
||||||
DELETE_ENVIRONMENT = "delete-environment",
|
DELETE_ENVIRONMENT = "delete-environment",
|
||||||
@@ -286,25 +269,6 @@ interface DeleteIntegrationEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ManualSyncIntegrationEvent {
|
|
||||||
type: EventType.MANUAL_SYNC_INTEGRATION;
|
|
||||||
metadata: {
|
|
||||||
integrationId: string;
|
|
||||||
integration: string;
|
|
||||||
environment: string;
|
|
||||||
secretPath: string;
|
|
||||||
url?: string;
|
|
||||||
app?: string;
|
|
||||||
appId?: string;
|
|
||||||
targetEnvironment?: string;
|
|
||||||
targetEnvironmentId?: string;
|
|
||||||
targetService?: string;
|
|
||||||
targetServiceId?: string;
|
|
||||||
path?: string;
|
|
||||||
region?: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AddTrustedIPEvent {
|
interface AddTrustedIPEvent {
|
||||||
type: EventType.ADD_TRUSTED_IP;
|
type: EventType.ADD_TRUSTED_IP;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -419,50 +383,6 @@ interface GetIdentityUniversalAuthEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LoginIdentityKubernetesAuthEvent {
|
|
||||||
type: EventType.LOGIN_IDENTITY_KUBERNETES_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
identityKubernetesAuthId: string;
|
|
||||||
identityAccessTokenId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AddIdentityKubernetesAuthEvent {
|
|
||||||
type: EventType.ADD_IDENTITY_KUBERNETES_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
kubernetesHost: string;
|
|
||||||
allowedNamespaces: string;
|
|
||||||
allowedNames: string;
|
|
||||||
accessTokenTTL: number;
|
|
||||||
accessTokenMaxTTL: number;
|
|
||||||
accessTokenNumUsesLimit: number;
|
|
||||||
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UpdateIdentityKubernetesAuthEvent {
|
|
||||||
type: EventType.UPDATE_IDENTITY_KUBENETES_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
kubernetesHost?: string;
|
|
||||||
allowedNamespaces?: string;
|
|
||||||
allowedNames?: string;
|
|
||||||
accessTokenTTL?: number;
|
|
||||||
accessTokenMaxTTL?: number;
|
|
||||||
accessTokenNumUsesLimit?: number;
|
|
||||||
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetIdentityKubernetesAuthEvent {
|
|
||||||
type: EventType.GET_IDENTITY_KUBERNETES_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CreateIdentityUniversalAuthClientSecretEvent {
|
interface CreateIdentityUniversalAuthClientSecretEvent {
|
||||||
type: EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET;
|
type: EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -486,138 +406,6 @@ interface RevokeIdentityUniversalAuthClientSecretEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LoginIdentityGcpAuthEvent {
|
|
||||||
type: EventType.LOGIN_IDENTITY_GCP_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
identityGcpAuthId: string;
|
|
||||||
identityAccessTokenId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AddIdentityGcpAuthEvent {
|
|
||||||
type: EventType.ADD_IDENTITY_GCP_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
type: string;
|
|
||||||
allowedServiceAccounts: string;
|
|
||||||
allowedProjects: string;
|
|
||||||
allowedZones: string;
|
|
||||||
accessTokenTTL: number;
|
|
||||||
accessTokenMaxTTL: number;
|
|
||||||
accessTokenNumUsesLimit: number;
|
|
||||||
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UpdateIdentityGcpAuthEvent {
|
|
||||||
type: EventType.UPDATE_IDENTITY_GCP_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
type?: string;
|
|
||||||
allowedServiceAccounts?: string;
|
|
||||||
allowedProjects?: string;
|
|
||||||
allowedZones?: string;
|
|
||||||
accessTokenTTL?: number;
|
|
||||||
accessTokenMaxTTL?: number;
|
|
||||||
accessTokenNumUsesLimit?: number;
|
|
||||||
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetIdentityGcpAuthEvent {
|
|
||||||
type: EventType.GET_IDENTITY_GCP_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LoginIdentityAwsAuthEvent {
|
|
||||||
type: EventType.LOGIN_IDENTITY_AWS_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
identityAwsAuthId: string;
|
|
||||||
identityAccessTokenId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AddIdentityAwsAuthEvent {
|
|
||||||
type: EventType.ADD_IDENTITY_AWS_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
stsEndpoint: string;
|
|
||||||
allowedPrincipalArns: string;
|
|
||||||
allowedAccountIds: string;
|
|
||||||
accessTokenTTL: number;
|
|
||||||
accessTokenMaxTTL: number;
|
|
||||||
accessTokenNumUsesLimit: number;
|
|
||||||
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UpdateIdentityAwsAuthEvent {
|
|
||||||
type: EventType.UPDATE_IDENTITY_AWS_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
stsEndpoint?: string;
|
|
||||||
allowedPrincipalArns?: string;
|
|
||||||
allowedAccountIds?: string;
|
|
||||||
accessTokenTTL?: number;
|
|
||||||
accessTokenMaxTTL?: number;
|
|
||||||
accessTokenNumUsesLimit?: number;
|
|
||||||
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetIdentityAwsAuthEvent {
|
|
||||||
type: EventType.GET_IDENTITY_AWS_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LoginIdentityAzureAuthEvent {
|
|
||||||
type: EventType.LOGIN_IDENTITY_AZURE_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
identityAzureAuthId: string;
|
|
||||||
identityAccessTokenId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AddIdentityAzureAuthEvent {
|
|
||||||
type: EventType.ADD_IDENTITY_AZURE_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
tenantId: string;
|
|
||||||
resource: string;
|
|
||||||
accessTokenTTL: number;
|
|
||||||
accessTokenMaxTTL: number;
|
|
||||||
accessTokenNumUsesLimit: number;
|
|
||||||
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UpdateIdentityAzureAuthEvent {
|
|
||||||
type: EventType.UPDATE_IDENTITY_AZURE_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
tenantId?: string;
|
|
||||||
resource?: string;
|
|
||||||
accessTokenTTL?: number;
|
|
||||||
accessTokenMaxTTL?: number;
|
|
||||||
accessTokenNumUsesLimit?: number;
|
|
||||||
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetIdentityAzureAuthEvent {
|
|
||||||
type: EventType.GET_IDENTITY_AZURE_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CreateEnvironmentEvent {
|
interface CreateEnvironmentEvent {
|
||||||
type: EventType.CREATE_ENVIRONMENT;
|
type: EventType.CREATE_ENVIRONMENT;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -837,9 +625,9 @@ interface SecretApprovalReopened {
|
|||||||
interface SecretApprovalRequest {
|
interface SecretApprovalRequest {
|
||||||
type: EventType.SECRET_APPROVAL_REQUEST;
|
type: EventType.SECRET_APPROVAL_REQUEST;
|
||||||
metadata: {
|
metadata: {
|
||||||
committedBy: string;
|
|
||||||
secretApprovalRequestSlug: string;
|
secretApprovalRequestSlug: string;
|
||||||
secretApprovalRequestId: string;
|
secretApprovalRequestId: string;
|
||||||
|
committedByUser?: string | null; // Needs to be nullable for backward compatibility
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -857,7 +645,6 @@ export type Event =
|
|||||||
| UnauthorizeIntegrationEvent
|
| UnauthorizeIntegrationEvent
|
||||||
| CreateIntegrationEvent
|
| CreateIntegrationEvent
|
||||||
| DeleteIntegrationEvent
|
| DeleteIntegrationEvent
|
||||||
| ManualSyncIntegrationEvent
|
|
||||||
| AddTrustedIPEvent
|
| AddTrustedIPEvent
|
||||||
| UpdateTrustedIPEvent
|
| UpdateTrustedIPEvent
|
||||||
| DeleteTrustedIPEvent
|
| DeleteTrustedIPEvent
|
||||||
@@ -870,25 +657,9 @@ export type Event =
|
|||||||
| AddIdentityUniversalAuthEvent
|
| AddIdentityUniversalAuthEvent
|
||||||
| UpdateIdentityUniversalAuthEvent
|
| UpdateIdentityUniversalAuthEvent
|
||||||
| GetIdentityUniversalAuthEvent
|
| GetIdentityUniversalAuthEvent
|
||||||
| LoginIdentityKubernetesAuthEvent
|
|
||||||
| AddIdentityKubernetesAuthEvent
|
|
||||||
| UpdateIdentityKubernetesAuthEvent
|
|
||||||
| GetIdentityKubernetesAuthEvent
|
|
||||||
| CreateIdentityUniversalAuthClientSecretEvent
|
| CreateIdentityUniversalAuthClientSecretEvent
|
||||||
| GetIdentityUniversalAuthClientSecretsEvent
|
| GetIdentityUniversalAuthClientSecretsEvent
|
||||||
| RevokeIdentityUniversalAuthClientSecretEvent
|
| RevokeIdentityUniversalAuthClientSecretEvent
|
||||||
| LoginIdentityGcpAuthEvent
|
|
||||||
| AddIdentityGcpAuthEvent
|
|
||||||
| UpdateIdentityGcpAuthEvent
|
|
||||||
| GetIdentityGcpAuthEvent
|
|
||||||
| LoginIdentityAwsAuthEvent
|
|
||||||
| AddIdentityAwsAuthEvent
|
|
||||||
| UpdateIdentityAwsAuthEvent
|
|
||||||
| GetIdentityAwsAuthEvent
|
|
||||||
| LoginIdentityAzureAuthEvent
|
|
||||||
| AddIdentityAzureAuthEvent
|
|
||||||
| UpdateIdentityAzureAuthEvent
|
|
||||||
| GetIdentityAzureAuthEvent
|
|
||||||
| CreateEnvironmentEvent
|
| CreateEnvironmentEvent
|
||||||
| UpdateEnvironmentEvent
|
| UpdateEnvironmentEvent
|
||||||
| DeleteEnvironmentEvent
|
| DeleteEnvironmentEvent
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
import { TDbClient } from "@app/db";
|
||||||
|
import { TableName } from "@app/db/schemas";
|
||||||
|
import { ormify } from "@app/lib/knex";
|
||||||
|
|
||||||
|
export type TGroupProjectUserAdditionalPrivilegeDALFactory = ReturnType<
|
||||||
|
typeof groupProjectUserAdditionalPrivilegeDALFactory
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const groupProjectUserAdditionalPrivilegeDALFactory = (db: TDbClient) => {
|
||||||
|
const orm = ormify(db, TableName.GroupProjectUserAdditionalPrivilege);
|
||||||
|
return orm;
|
||||||
|
};
|
@@ -5,10 +5,78 @@ import { TableName, TGroups } from "@app/db/schemas";
|
|||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt } from "@app/lib/knex";
|
import { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt } from "@app/lib/knex";
|
||||||
|
|
||||||
|
import { TUserGroupMembershipDALFactory } from "./user-group-membership-dal";
|
||||||
|
|
||||||
export type TGroupDALFactory = ReturnType<typeof groupDALFactory>;
|
export type TGroupDALFactory = ReturnType<typeof groupDALFactory>;
|
||||||
|
|
||||||
export const groupDALFactory = (db: TDbClient) => {
|
export const groupDALFactory = (db: TDbClient, userGroupMembershipDAL: TUserGroupMembershipDALFactory) => {
|
||||||
const groupOrm = ormify(db, TableName.Groups);
|
const groupOrm = ormify(db, TableName.Groups);
|
||||||
|
const groupMembershipOrm = ormify(db, TableName.GroupProjectMembership);
|
||||||
|
const accessApprovalRequestOrm = ormify(db, TableName.AccessApprovalRequest);
|
||||||
|
const secretApprovalRequestOrm = ormify(db, TableName.SecretApprovalRequest);
|
||||||
|
|
||||||
|
const deleteMany = async (filterQuery: TFindFilter<TGroups>, tx?: Knex) => {
|
||||||
|
const transaction = tx || (await db.transaction());
|
||||||
|
|
||||||
|
// Find all memberships
|
||||||
|
const groups = await groupOrm.find(filterQuery, { tx: transaction });
|
||||||
|
|
||||||
|
for await (const group of groups) {
|
||||||
|
// Find all the group memberships of the groups (a group membership is which projects the group is a part of)
|
||||||
|
const groupProjectMemberships = await groupMembershipOrm.find(
|
||||||
|
{ groupId: group.id },
|
||||||
|
{
|
||||||
|
tx: transaction
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// For each of those group memberships, we need to find all the members of the group that don't have a regular membership in the project
|
||||||
|
for await (const groupMembership of groupProjectMemberships) {
|
||||||
|
const members = await userGroupMembershipDAL.findGroupMembersNotInProject(
|
||||||
|
group.id,
|
||||||
|
groupMembership.projectId,
|
||||||
|
transaction
|
||||||
|
);
|
||||||
|
|
||||||
|
// We then delete all the access approval requests and secret approval requests associated with these members
|
||||||
|
await accessApprovalRequestOrm.delete(
|
||||||
|
{
|
||||||
|
groupMembershipId: groupMembership.id,
|
||||||
|
$in: {
|
||||||
|
requestedByUserId: members.map(({ user }) => user.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transaction
|
||||||
|
);
|
||||||
|
|
||||||
|
const policies = await (tx || db)(TableName.SecretApprovalPolicy)
|
||||||
|
.join(TableName.Environment, `${TableName.SecretApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
||||||
|
.where(`${TableName.Environment}.projectId`, groupMembership.projectId)
|
||||||
|
.select(selectAllTableCols(TableName.SecretApprovalPolicy));
|
||||||
|
|
||||||
|
await secretApprovalRequestOrm.delete(
|
||||||
|
{
|
||||||
|
$in: {
|
||||||
|
policyId: policies.map(({ id }) => id),
|
||||||
|
committerUserId: members.map(({ user }) => user.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transaction
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await groupOrm.delete(
|
||||||
|
{
|
||||||
|
$in: {
|
||||||
|
id: groups.map((group) => group.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transaction
|
||||||
|
);
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
};
|
||||||
|
|
||||||
const findGroups = async (filter: TFindFilter<TGroups>, { offset, limit, sort, tx }: TFindOpt<TGroups> = {}) => {
|
const findGroups = async (filter: TFindFilter<TGroups>, { offset, limit, sort, tx }: TFindOpt<TGroups> = {}) => {
|
||||||
try {
|
try {
|
||||||
@@ -122,9 +190,10 @@ export const groupDALFactory = (db: TDbClient) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
...groupOrm,
|
||||||
findGroups,
|
findGroups,
|
||||||
findByOrgId,
|
findByOrgId,
|
||||||
findAllGroupMembers,
|
findAllGroupMembers,
|
||||||
...groupOrm
|
delete: deleteMany
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName, TUsers } from "@app/db/schemas";
|
import { SecretKeyEncoding, TUsers } from "@app/db/schemas";
|
||||||
import { decryptAsymmetric, encryptAsymmetric, infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
import { decryptAsymmetric, encryptAsymmetric, infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
||||||
import { BadRequestError, ScimRequestError } from "@app/lib/errors";
|
import { BadRequestError, ScimRequestError } from "@app/lib/errors";
|
||||||
|
|
||||||
@@ -188,9 +188,9 @@ export const addUsersToGroupByUserIds = async ({
|
|||||||
// check if all user(s) are part of the organization
|
// check if all user(s) are part of the organization
|
||||||
const existingUserOrgMemberships = await orgDAL.findMembership(
|
const existingUserOrgMemberships = await orgDAL.findMembership(
|
||||||
{
|
{
|
||||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: group.orgId,
|
orgId: group.orgId,
|
||||||
$in: {
|
$in: {
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: userIds
|
userId: userIds
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
@@ -266,6 +266,9 @@ export const removeUsersFromGroupByUserIds = async ({
|
|||||||
userIds,
|
userIds,
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
|
accessApprovalRequestDAL,
|
||||||
|
secretApprovalRequestDAL,
|
||||||
|
secretApprovalPolicyDAL,
|
||||||
groupProjectDAL,
|
groupProjectDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
tx: outerTx
|
tx: outerTx
|
||||||
@@ -322,20 +325,16 @@ export const removeUsersFromGroupByUserIds = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (membersToRemoveFromGroupNonPending.length) {
|
if (membersToRemoveFromGroupNonPending.length) {
|
||||||
// check which projects the group is part of
|
const groupProjectMemberships = await groupProjectDAL.find(
|
||||||
const projectIds = Array.from(
|
{
|
||||||
new Set(
|
groupId: group.id
|
||||||
(
|
},
|
||||||
await groupProjectDAL.find(
|
{ tx }
|
||||||
{
|
|
||||||
groupId: group.id
|
|
||||||
},
|
|
||||||
{ tx }
|
|
||||||
)
|
|
||||||
).map((gp) => gp.projectId)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// check which projects the group is part of
|
||||||
|
const projectIds = Array.from(new Set(groupProjectMemberships.map((gp) => gp.projectId)));
|
||||||
|
|
||||||
// TODO: this part can be optimized
|
// TODO: this part can be optimized
|
||||||
for await (const userId of userIds) {
|
for await (const userId of userIds) {
|
||||||
const t = await userGroupMembershipDAL.filterProjectsByUserMembership(userId, group.id, projectIds, tx);
|
const t = await userGroupMembershipDAL.filterProjectsByUserMembership(userId, group.id, projectIds, tx);
|
||||||
@@ -353,10 +352,35 @@ export const removeUsersFromGroupByUserIds = async ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await accessApprovalRequestDAL.delete(
|
||||||
|
{
|
||||||
|
$in: {
|
||||||
|
groupMembershipId: groupProjectMemberships
|
||||||
|
.filter((gp) => projectsToDeleteKeyFor.includes(gp.projectId))
|
||||||
|
.map((gp) => gp.id)
|
||||||
|
},
|
||||||
|
requestedByUserId: userId
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
const projectSecretApprovalPolicies = await secretApprovalPolicyDAL.findByProjectIds(projectIds);
|
||||||
|
await secretApprovalRequestDAL.delete(
|
||||||
|
{
|
||||||
|
committerUserId: userId,
|
||||||
|
$in: {
|
||||||
|
policyId: projectSecretApprovalPolicies.map((p) => p.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
await userGroupMembershipDAL.delete(
|
await userGroupMembershipDAL.delete(
|
||||||
{
|
{
|
||||||
groupId: group.id,
|
groupId: group.id,
|
||||||
userId
|
$in: {
|
||||||
|
userId: membersToRemoveFromGroupNonPending.map((member) => member.id)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -364,12 +388,15 @@ export const removeUsersFromGroupByUserIds = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (membersToRemoveFromGroupPending.length) {
|
if (membersToRemoveFromGroupPending.length) {
|
||||||
await userGroupMembershipDAL.delete({
|
await userGroupMembershipDAL.delete(
|
||||||
groupId: group.id,
|
{
|
||||||
$in: {
|
groupId: group.id,
|
||||||
userId: membersToRemoveFromGroupPending.map((member) => member.id)
|
$in: {
|
||||||
}
|
userId: membersToRemoveFromGroupPending.map((member) => member.id)
|
||||||
});
|
}
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return membersToRemoveFromGroupNonPending.concat(membersToRemoveFromGroupPending);
|
return membersToRemoveFromGroupNonPending.concat(membersToRemoveFromGroupPending);
|
||||||
|
@@ -12,9 +12,12 @@ import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal
|
|||||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
|
import { TAccessApprovalRequestDALFactory } from "../access-approval-request/access-approval-request-dal";
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
|
import { TSecretApprovalPolicyDALFactory } from "../secret-approval-policy/secret-approval-policy-dal";
|
||||||
|
import { TSecretApprovalRequestDALFactory } from "../secret-approval-request/secret-approval-request-dal";
|
||||||
import { TGroupDALFactory } from "./group-dal";
|
import { TGroupDALFactory } from "./group-dal";
|
||||||
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "./group-fns";
|
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "./group-fns";
|
||||||
import {
|
import {
|
||||||
@@ -41,6 +44,9 @@ type TGroupServiceFactoryDep = {
|
|||||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "findLatestProjectKey" | "insertMany">;
|
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "findLatestProjectKey" | "insertMany">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRole">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRole">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
|
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "delete">;
|
||||||
|
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "delete">;
|
||||||
|
secretApprovalPolicyDAL: Pick<TSecretApprovalPolicyDALFactory, "findByProjectIds">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGroupServiceFactory = ReturnType<typeof groupServiceFactory>;
|
export type TGroupServiceFactory = ReturnType<typeof groupServiceFactory>;
|
||||||
@@ -50,6 +56,9 @@ export const groupServiceFactory = ({
|
|||||||
groupDAL,
|
groupDAL,
|
||||||
groupProjectDAL,
|
groupProjectDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
|
secretApprovalRequestDAL,
|
||||||
|
secretApprovalPolicyDAL,
|
||||||
|
accessApprovalRequestDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
@@ -328,6 +337,9 @@ export const groupServiceFactory = ({
|
|||||||
group,
|
group,
|
||||||
userIds: [user.id],
|
userIds: [user.id],
|
||||||
userDAL,
|
userDAL,
|
||||||
|
accessApprovalRequestDAL,
|
||||||
|
secretApprovalPolicyDAL,
|
||||||
|
secretApprovalRequestDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupProjectDAL,
|
groupProjectDAL,
|
||||||
projectKeyDAL
|
projectKeyDAL
|
||||||
|
@@ -10,6 +10,10 @@ import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal
|
|||||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
|
import { TAccessApprovalRequestDALFactory } from "../access-approval-request/access-approval-request-dal";
|
||||||
|
import { TSecretApprovalPolicyDALFactory } from "../secret-approval-policy/secret-approval-policy-dal";
|
||||||
|
import { TSecretApprovalRequestDALFactory } from "../secret-approval-request/secret-approval-request-dal";
|
||||||
|
|
||||||
export type TCreateGroupDTO = {
|
export type TCreateGroupDTO = {
|
||||||
name: string;
|
name: string;
|
||||||
slug?: string;
|
slug?: string;
|
||||||
@@ -77,6 +81,9 @@ export type TRemoveUsersFromGroupByUserIds = {
|
|||||||
group: TGroups;
|
group: TGroups;
|
||||||
userIds: string[];
|
userIds: string[];
|
||||||
userDAL: Pick<TUserDALFactory, "find" | "transaction">;
|
userDAL: Pick<TUserDALFactory, "find" | "transaction">;
|
||||||
|
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "delete">;
|
||||||
|
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "delete">;
|
||||||
|
secretApprovalPolicyDAL: Pick<TSecretApprovalPolicyDALFactory, "findByProjectIds">;
|
||||||
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "find" | "filterProjectsByUserMembership" | "delete">;
|
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "find" | "filterProjectsByUserMembership" | "delete">;
|
||||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
||||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "delete">;
|
projectKeyDAL: Pick<TProjectKeyDALFactory, "delete">;
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { PackRule, unpackRules } from "@casl/ability/extra";
|
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||||
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
|
||||||
@@ -10,7 +8,7 @@ import { TIdentityProjectDALFactory } from "@app/services/identity-project/ident
|
|||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSet, ProjectPermissionSub } from "../permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||||
import { TIdentityProjectAdditionalPrivilegeDALFactory } from "./identity-project-additional-privilege-dal";
|
import { TIdentityProjectAdditionalPrivilegeDALFactory } from "./identity-project-additional-privilege-dal";
|
||||||
import {
|
import {
|
||||||
IdentityProjectAdditionalPrivilegeTemporaryMode,
|
IdentityProjectAdditionalPrivilegeTemporaryMode,
|
||||||
@@ -32,27 +30,6 @@ export type TIdentityProjectAdditionalPrivilegeServiceFactory = ReturnType<
|
|||||||
typeof identityProjectAdditionalPrivilegeServiceFactory
|
typeof identityProjectAdditionalPrivilegeServiceFactory
|
||||||
>;
|
>;
|
||||||
|
|
||||||
// TODO(akhilmhdh): move this to more centralized
|
|
||||||
export const UnpackedPermissionSchema = z.object({
|
|
||||||
subject: z.union([z.string().min(1), z.string().array()]).optional(),
|
|
||||||
action: z.union([z.string().min(1), z.string().array()]),
|
|
||||||
conditions: z
|
|
||||||
.object({
|
|
||||||
environment: z.string().optional(),
|
|
||||||
secretPath: z
|
|
||||||
.object({
|
|
||||||
$glob: z.string().min(1)
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
const unpackPermissions = (permissions: unknown) =>
|
|
||||||
UnpackedPermissionSchema.array().parse(
|
|
||||||
unpackRules((permissions || []) as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[])
|
|
||||||
);
|
|
||||||
|
|
||||||
export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
||||||
identityProjectAdditionalPrivilegeDAL,
|
identityProjectAdditionalPrivilegeDAL,
|
||||||
identityProjectDAL,
|
identityProjectDAL,
|
||||||
@@ -109,10 +86,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
slug,
|
slug,
|
||||||
permissions: customPermission
|
permissions: customPermission
|
||||||
});
|
});
|
||||||
return {
|
return additionalPrivilege;
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
|
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
|
||||||
@@ -126,10 +100,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
|
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
|
||||||
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
|
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
|
||||||
});
|
});
|
||||||
return {
|
return additionalPrivilege;
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateBySlug = async ({
|
const updateBySlug = async ({
|
||||||
@@ -192,11 +163,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
|
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
|
||||||
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
|
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
|
||||||
});
|
});
|
||||||
return {
|
return additionalPrivilege;
|
||||||
...additionalPrivilege,
|
|
||||||
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
|
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
|
||||||
@@ -207,11 +174,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
temporaryRange: null,
|
temporaryRange: null,
|
||||||
temporaryMode: null
|
temporaryMode: null
|
||||||
});
|
});
|
||||||
return {
|
return additionalPrivilege;
|
||||||
...additionalPrivilege,
|
|
||||||
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteBySlug = async ({
|
const deleteBySlug = async ({
|
||||||
@@ -257,11 +220,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
||||||
|
|
||||||
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
|
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
|
||||||
return {
|
return deletedPrivilege;
|
||||||
...deletedPrivilege,
|
|
||||||
|
|
||||||
permissions: unpackPermissions(deletedPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPrivilegeDetailsBySlug = async ({
|
const getPrivilegeDetailsBySlug = async ({
|
||||||
@@ -295,10 +254,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
});
|
});
|
||||||
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
||||||
|
|
||||||
return {
|
return identityPrivilege;
|
||||||
...identityPrivilege,
|
|
||||||
permissions: unpackPermissions(identityPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const listIdentityProjectPrivileges = async ({
|
const listIdentityProjectPrivileges = async ({
|
||||||
@@ -328,11 +284,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find({
|
const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find({
|
||||||
projectMembershipId: identityProjectMembership.id
|
projectMembershipId: identityProjectMembership.id
|
||||||
});
|
});
|
||||||
return identityPrivileges.map((el) => ({
|
return identityPrivileges;
|
||||||
...el,
|
|
||||||
|
|
||||||
permissions: unpackPermissions(el.permissions)
|
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -1,14 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
|
|
||||||
import {
|
import { OrgMembershipRole, OrgMembershipStatus, SecretKeyEncoding, TLdapConfigsUpdate } from "@app/db/schemas";
|
||||||
OrgMembershipRole,
|
|
||||||
OrgMembershipStatus,
|
|
||||||
SecretKeyEncoding,
|
|
||||||
TableName,
|
|
||||||
TLdapConfigsUpdate,
|
|
||||||
TUsers
|
|
||||||
} from "@app/db/schemas";
|
|
||||||
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
||||||
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
|
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
|
||||||
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
||||||
@@ -26,19 +19,19 @@ import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
|||||||
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
||||||
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
|
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
||||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
import { normalizeUsername } from "@app/services/user/user-fns";
|
import { normalizeUsername } from "@app/services/user/user-fns";
|
||||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
||||||
import { UserAliasType } from "@app/services/user-alias/user-alias-types";
|
|
||||||
|
|
||||||
|
import { TAccessApprovalRequestDALFactory } from "../access-approval-request/access-approval-request-dal";
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
|
import { TSecretApprovalPolicyDALFactory } from "../secret-approval-policy/secret-approval-policy-dal";
|
||||||
|
import { TSecretApprovalRequestDALFactory } from "../secret-approval-request/secret-approval-request-dal";
|
||||||
import { TLdapConfigDALFactory } from "./ldap-config-dal";
|
import { TLdapConfigDALFactory } from "./ldap-config-dal";
|
||||||
import {
|
import {
|
||||||
TCreateLdapCfgDTO,
|
TCreateLdapCfgDTO,
|
||||||
@@ -56,7 +49,6 @@ import { TLdapGroupMapDALFactory } from "./ldap-group-map-dal";
|
|||||||
type TLdapConfigServiceFactoryDep = {
|
type TLdapConfigServiceFactoryDep = {
|
||||||
ldapConfigDAL: Pick<TLdapConfigDALFactory, "create" | "update" | "findOne">;
|
ldapConfigDAL: Pick<TLdapConfigDALFactory, "create" | "update" | "findOne">;
|
||||||
ldapGroupMapDAL: Pick<TLdapGroupMapDALFactory, "find" | "create" | "delete" | "findLdapGroupMapsByLdapConfigId">;
|
ldapGroupMapDAL: Pick<TLdapGroupMapDALFactory, "find" | "create" | "delete" | "findLdapGroupMapsByLdapConfigId">;
|
||||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">;
|
|
||||||
orgDAL: Pick<
|
orgDAL: Pick<
|
||||||
TOrgDALFactory,
|
TOrgDALFactory,
|
||||||
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
|
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
|
||||||
@@ -78,6 +70,9 @@ type TLdapConfigServiceFactoryDep = {
|
|||||||
userAliasDAL: Pick<TUserAliasDALFactory, "create" | "findOne">;
|
userAliasDAL: Pick<TUserAliasDALFactory, "create" | "findOne">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
|
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "delete">;
|
||||||
|
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "delete">;
|
||||||
|
secretApprovalPolicyDAL: Pick<TSecretApprovalPolicyDALFactory, "findByProjectIds">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TLdapConfigServiceFactory = ReturnType<typeof ldapConfigServiceFactory>;
|
export type TLdapConfigServiceFactory = ReturnType<typeof ldapConfigServiceFactory>;
|
||||||
@@ -86,10 +81,12 @@ export const ldapConfigServiceFactory = ({
|
|||||||
ldapConfigDAL,
|
ldapConfigDAL,
|
||||||
ldapGroupMapDAL,
|
ldapGroupMapDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
orgBotDAL,
|
orgBotDAL,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
groupProjectDAL,
|
groupProjectDAL,
|
||||||
|
accessApprovalRequestDAL,
|
||||||
|
secretApprovalPolicyDAL,
|
||||||
|
secretApprovalRequestDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
@@ -391,17 +388,16 @@ export const ldapConfigServiceFactory = ({
|
|||||||
username,
|
username,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
email,
|
emails,
|
||||||
groups,
|
groups,
|
||||||
orgId,
|
orgId,
|
||||||
relayState
|
relayState
|
||||||
}: TLdapLoginDTO) => {
|
}: TLdapLoginDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const serverCfg = await getServerCfg();
|
|
||||||
let userAlias = await userAliasDAL.findOne({
|
let userAlias = await userAliasDAL.findOne({
|
||||||
externalId,
|
externalId,
|
||||||
orgId,
|
orgId,
|
||||||
aliasType: UserAliasType.LDAP
|
aliasType: AuthMethod.LDAP
|
||||||
});
|
});
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
@@ -409,13 +405,7 @@ export const ldapConfigServiceFactory = ({
|
|||||||
|
|
||||||
if (userAlias) {
|
if (userAlias) {
|
||||||
await userDAL.transaction(async (tx) => {
|
await userDAL.transaction(async (tx) => {
|
||||||
const [orgMembership] = await orgDAL.findMembership(
|
const [orgMembership] = await orgDAL.findMembership({ userId: userAlias.userId }, { tx });
|
||||||
{
|
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: userAlias.userId,
|
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
|
||||||
},
|
|
||||||
{ tx }
|
|
||||||
);
|
|
||||||
if (!orgMembership) {
|
if (!orgMembership) {
|
||||||
await orgDAL.createMembership(
|
await orgDAL.createMembership(
|
||||||
{
|
{
|
||||||
@@ -438,75 +428,40 @@ export const ldapConfigServiceFactory = ({
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
userAlias = await userDAL.transaction(async (tx) => {
|
userAlias = await userDAL.transaction(async (tx) => {
|
||||||
let newUser: TUsers | undefined;
|
const uniqueUsername = await normalizeUsername(username, userDAL);
|
||||||
if (serverCfg.trustSamlEmails) {
|
const newUser = await userDAL.create(
|
||||||
newUser = await userDAL.findOne(
|
{
|
||||||
{
|
username: uniqueUsername,
|
||||||
email,
|
email: emails[0],
|
||||||
isEmailVerified: true
|
firstName,
|
||||||
},
|
lastName,
|
||||||
tx
|
authMethods: [AuthMethod.LDAP],
|
||||||
);
|
isGhost: false
|
||||||
}
|
},
|
||||||
|
tx
|
||||||
if (!newUser) {
|
);
|
||||||
const uniqueUsername = await normalizeUsername(username, userDAL);
|
|
||||||
newUser = await userDAL.create(
|
|
||||||
{
|
|
||||||
username: serverCfg.trustLdapEmails ? email : uniqueUsername,
|
|
||||||
email,
|
|
||||||
isEmailVerified: serverCfg.trustLdapEmails,
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
authMethods: [],
|
|
||||||
isGhost: false
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const newUserAlias = await userAliasDAL.create(
|
const newUserAlias = await userAliasDAL.create(
|
||||||
{
|
{
|
||||||
userId: newUser.id,
|
userId: newUser.id,
|
||||||
username,
|
username,
|
||||||
aliasType: UserAliasType.LDAP,
|
aliasType: AuthMethod.LDAP,
|
||||||
externalId,
|
externalId,
|
||||||
emails: [email],
|
emails,
|
||||||
orgId
|
orgId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
const [orgMembership] = await orgDAL.findMembership(
|
await orgDAL.createMembership(
|
||||||
{
|
{
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: newUser.id,
|
userId: newUser.id,
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
orgId,
|
||||||
|
role: OrgMembershipRole.Member,
|
||||||
|
status: OrgMembershipStatus.Invited
|
||||||
},
|
},
|
||||||
{ tx }
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!orgMembership) {
|
|
||||||
await orgMembershipDAL.create(
|
|
||||||
{
|
|
||||||
userId: userAlias.userId,
|
|
||||||
inviteEmail: email,
|
|
||||||
orgId,
|
|
||||||
role: OrgMembershipRole.Member,
|
|
||||||
status: newUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
// Only update the membership to Accepted if the user account is already completed.
|
|
||||||
} else if (orgMembership.status === OrgMembershipStatus.Invited && newUser.isAccepted) {
|
|
||||||
await orgDAL.updateMembershipById(
|
|
||||||
orgMembership.id,
|
|
||||||
{
|
|
||||||
status: OrgMembershipStatus.Accepted
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newUserAlias;
|
return newUserAlias;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -578,7 +533,10 @@ export const ldapConfigServiceFactory = ({
|
|||||||
group,
|
group,
|
||||||
userIds: [newUser.id],
|
userIds: [newUser.id],
|
||||||
userDAL,
|
userDAL,
|
||||||
|
secretApprovalRequestDAL,
|
||||||
|
accessApprovalRequestDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
|
secretApprovalPolicyDAL,
|
||||||
groupProjectDAL,
|
groupProjectDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
tx
|
tx
|
||||||
@@ -597,14 +555,11 @@ export const ldapConfigServiceFactory = ({
|
|||||||
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
...(user.email && { email: user.email, isEmailVerified: user.isEmailVerified }),
|
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
organizationName: organization.name,
|
organizationName: organization.name,
|
||||||
organizationId: organization.id,
|
organizationId: organization.id,
|
||||||
organizationSlug: organization.slug,
|
|
||||||
authMethod: AuthMethod.LDAP,
|
authMethod: AuthMethod.LDAP,
|
||||||
authType: UserAliasType.LDAP,
|
|
||||||
isUserCompleted,
|
isUserCompleted,
|
||||||
...(relayState
|
...(relayState
|
||||||
? {
|
? {
|
||||||
|
@@ -51,7 +51,7 @@ export type TLdapLoginDTO = {
|
|||||||
username: string;
|
username: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
email: string;
|
emails: string[];
|
||||||
orgId: string;
|
orgId: string;
|
||||||
groups?: {
|
groups?: {
|
||||||
dn: string;
|
dn: string;
|
||||||
|
@@ -16,8 +16,6 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
void bd.where({ orgId });
|
void bd.where({ orgId });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
|
||||||
.where(`${TableName.Users}.isGhost`, false)
|
|
||||||
.count();
|
.count();
|
||||||
return doc?.[0].count;
|
return doc?.[0].count;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@@ -62,6 +62,11 @@ export const permissionDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.GroupProjectMembershipRole}.projectMembershipId`,
|
`${TableName.GroupProjectMembershipRole}.projectMembershipId`,
|
||||||
`${TableName.GroupProjectMembership}.id`
|
`${TableName.GroupProjectMembership}.id`
|
||||||
)
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.GroupProjectUserAdditionalPrivilege,
|
||||||
|
`${TableName.GroupProjectUserAdditionalPrivilege}.groupProjectMembershipId`,
|
||||||
|
`${TableName.GroupProjectMembership}.id`
|
||||||
|
)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.ProjectRoles,
|
TableName.ProjectRoles,
|
||||||
`${TableName.GroupProjectMembershipRole}.customRoleId`,
|
`${TableName.GroupProjectMembershipRole}.customRoleId`,
|
||||||
@@ -77,11 +82,34 @@ export const permissionDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("projectId").withSchema(TableName.GroupProjectMembership),
|
db.ref("projectId").withSchema(TableName.GroupProjectMembership),
|
||||||
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
|
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
|
||||||
db.ref("orgId").withSchema(TableName.Project),
|
db.ref("orgId").withSchema(TableName.Project),
|
||||||
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug")
|
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
|
||||||
|
db.ref("permissions").withSchema(TableName.ProjectRoles)
|
||||||
)
|
)
|
||||||
.select("permissions");
|
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
||||||
|
.select(
|
||||||
|
db.ref("projectId").withSchema(TableName.GroupProjectMembership).as("groupMembershipProjectId"),
|
||||||
|
db.ref("id").withSchema(TableName.GroupProjectUserAdditionalPrivilege).as("userApId"),
|
||||||
|
db.ref("permissions").withSchema(TableName.GroupProjectUserAdditionalPrivilege).as("userApPermissions"),
|
||||||
|
db.ref("temporaryMode").withSchema(TableName.GroupProjectUserAdditionalPrivilege).as("userApTemporaryMode"),
|
||||||
|
db.ref("isTemporary").withSchema(TableName.GroupProjectUserAdditionalPrivilege).as("userApIsTemporary"),
|
||||||
|
db.ref("temporaryRange").withSchema(TableName.GroupProjectUserAdditionalPrivilege).as("userApTemporaryRange"),
|
||||||
|
db.ref("groupProjectMembershipId").withSchema(TableName.GroupProjectUserAdditionalPrivilege),
|
||||||
|
db
|
||||||
|
.ref("requestedByUserId")
|
||||||
|
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
|
||||||
|
.as("userApRequestedByUserId"),
|
||||||
|
|
||||||
const docs = await db(TableName.ProjectMembership)
|
db
|
||||||
|
.ref("temporaryAccessStartTime")
|
||||||
|
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
|
||||||
|
.as("userApTemporaryAccessStartTime"),
|
||||||
|
db
|
||||||
|
.ref("temporaryAccessEndTime")
|
||||||
|
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
|
||||||
|
.as("userApTemporaryAccessEndTime")
|
||||||
|
);
|
||||||
|
|
||||||
|
const projectMemberDocs = await db(TableName.ProjectMembership)
|
||||||
.join(
|
.join(
|
||||||
TableName.ProjectUserMembershipRole,
|
TableName.ProjectUserMembershipRole,
|
||||||
`${TableName.ProjectUserMembershipRole}.projectMembershipId`,
|
`${TableName.ProjectUserMembershipRole}.projectMembershipId`,
|
||||||
@@ -127,7 +155,7 @@ export const permissionDALFactory = (db: TDbClient) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const permission = sqlNestRelationships({
|
const permission = sqlNestRelationships({
|
||||||
data: docs,
|
data: projectMemberDocs,
|
||||||
key: "projectId",
|
key: "projectId",
|
||||||
parentMapper: ({ orgId, orgAuthEnforced, membershipId, membershipCreatedAt, membershipUpdatedAt }) => ({
|
parentMapper: ({ orgId, orgAuthEnforced, membershipId, membershipCreatedAt, membershipUpdatedAt }) => ({
|
||||||
orgId,
|
orgId,
|
||||||
@@ -194,6 +222,33 @@ export const permissionDALFactory = (db: TDbClient) => {
|
|||||||
permissions: z.unknown(),
|
permissions: z.unknown(),
|
||||||
customRoleSlug: z.string().optional().nullable()
|
customRoleSlug: z.string().optional().nullable()
|
||||||
}).parse(data)
|
}).parse(data)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "userApId",
|
||||||
|
label: "additionalPrivileges" as const,
|
||||||
|
mapper: ({
|
||||||
|
groupMembershipProjectId,
|
||||||
|
groupProjectMembershipId,
|
||||||
|
userApId,
|
||||||
|
userApPermissions,
|
||||||
|
userApRequestedByUserId,
|
||||||
|
userApIsTemporary,
|
||||||
|
userApTemporaryMode,
|
||||||
|
userApTemporaryRange,
|
||||||
|
userApTemporaryAccessEndTime,
|
||||||
|
userApTemporaryAccessStartTime
|
||||||
|
}) => ({
|
||||||
|
groupProjectMembershipId,
|
||||||
|
groupMembershipProjectId,
|
||||||
|
id: userApId,
|
||||||
|
userId: userApRequestedByUserId,
|
||||||
|
permissions: userApPermissions,
|
||||||
|
temporaryRange: userApTemporaryRange,
|
||||||
|
temporaryMode: userApTemporaryMode,
|
||||||
|
temporaryAccessEndTime: userApTemporaryAccessEndTime,
|
||||||
|
temporaryAccessStartTime: userApTemporaryAccessStartTime,
|
||||||
|
isTemporary: userApIsTemporary
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@@ -214,15 +269,24 @@ export const permissionDALFactory = (db: TDbClient) => {
|
|||||||
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
||||||
) ?? [];
|
) ?? [];
|
||||||
|
|
||||||
const activeAdditionalPrivileges = permission?.[0]?.additionalPrivileges?.filter(
|
const activeAdditionalPrivileges =
|
||||||
({ isTemporary, temporaryAccessEndTime }) =>
|
permission?.[0]?.additionalPrivileges?.filter(
|
||||||
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
({ isTemporary, temporaryAccessEndTime }) =>
|
||||||
);
|
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
||||||
|
) ?? [];
|
||||||
|
const activeGroupAdditionalPrivileges =
|
||||||
|
groupPermission?.[0]?.additionalPrivileges?.filter(
|
||||||
|
({ isTemporary, temporaryAccessEndTime, groupProjectMembershipId, groupMembershipProjectId, userId: user }) =>
|
||||||
|
groupMembershipProjectId === projectId &&
|
||||||
|
!!groupProjectMembershipId &&
|
||||||
|
user === userId &&
|
||||||
|
(!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime))
|
||||||
|
) ?? [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...(permission[0] || groupPermission[0]),
|
...(permission[0] || groupPermission[0]),
|
||||||
roles: [...activeRoles, ...activeGroupRoles],
|
roles: [...activeRoles, ...activeGroupRoles],
|
||||||
additionalPrivileges: activeAdditionalPrivileges
|
additionalPrivileges: [...activeAdditionalPrivileges, ...activeGroupAdditionalPrivileges]
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "GetProjectPermission" });
|
throw new DatabaseError({ error, name: "GetProjectPermission" });
|
||||||
|
@@ -90,6 +90,10 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
||||||
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
|
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
|
||||||
|
|
||||||
|
// This is fine. This service is only used for direct user privileges, not group-based privileges
|
||||||
|
if (!userPrivilege.projectMembershipId)
|
||||||
|
throw new BadRequestError({ message: "Operation not supported for groups" });
|
||||||
|
|
||||||
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
|
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
|
||||||
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
|
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
|
||||||
|
|
||||||
@@ -138,6 +142,10 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
||||||
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
|
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
|
||||||
|
|
||||||
|
// This is fine. This service is only used for direct user privileges, not group-based privileges
|
||||||
|
if (!userPrivilege.projectMembershipId)
|
||||||
|
throw new BadRequestError({ message: "Operation not supported for groups" });
|
||||||
|
|
||||||
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
|
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
|
||||||
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
|
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
|
||||||
|
|
||||||
@@ -164,6 +172,10 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
||||||
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
|
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
|
||||||
|
|
||||||
|
// This is fine. This service is only used for direct user privileges, not group-based privileges
|
||||||
|
if (!userPrivilege.projectMembershipId)
|
||||||
|
throw new BadRequestError({ message: "Operation not supported for groups" });
|
||||||
|
|
||||||
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
|
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
|
||||||
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
|
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
|
||||||
|
|
||||||
|
@@ -7,8 +7,7 @@ import {
|
|||||||
SecretKeyEncoding,
|
SecretKeyEncoding,
|
||||||
TableName,
|
TableName,
|
||||||
TSamlConfigs,
|
TSamlConfigs,
|
||||||
TSamlConfigsUpdate,
|
TSamlConfigsUpdate
|
||||||
TUsers
|
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import {
|
import {
|
||||||
@@ -20,18 +19,10 @@ import {
|
|||||||
infisicalSymmetricEncypt
|
infisicalSymmetricEncypt
|
||||||
} from "@app/lib/crypto/encryption";
|
} from "@app/lib/crypto/encryption";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { AuthTokenType } from "@app/services/auth/auth-type";
|
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
|
||||||
import { TokenType } from "@app/services/auth-token/auth-token-types";
|
|
||||||
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
|
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
|
||||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
import { normalizeUsername } from "@app/services/user/user-fns";
|
|
||||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
|
||||||
import { UserAliasType } from "@app/services/user-alias/user-alias-types";
|
|
||||||
|
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
@@ -40,19 +31,15 @@ import { TSamlConfigDALFactory } from "./saml-config-dal";
|
|||||||
import { TCreateSamlCfgDTO, TGetSamlCfgDTO, TSamlLoginDTO, TUpdateSamlCfgDTO } from "./saml-config-types";
|
import { TCreateSamlCfgDTO, TGetSamlCfgDTO, TSamlLoginDTO, TUpdateSamlCfgDTO } from "./saml-config-types";
|
||||||
|
|
||||||
type TSamlConfigServiceFactoryDep = {
|
type TSamlConfigServiceFactoryDep = {
|
||||||
samlConfigDAL: Pick<TSamlConfigDALFactory, "create" | "findOne" | "update" | "findById">;
|
samlConfigDAL: TSamlConfigDALFactory;
|
||||||
userDAL: Pick<TUserDALFactory, "create" | "findOne" | "transaction" | "updateById" | "findById">;
|
userDAL: Pick<TUserDALFactory, "create" | "findOne" | "transaction" | "updateById">;
|
||||||
userAliasDAL: Pick<TUserAliasDALFactory, "create" | "findOne">;
|
|
||||||
orgDAL: Pick<
|
orgDAL: Pick<
|
||||||
TOrgDALFactory,
|
TOrgDALFactory,
|
||||||
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
|
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
|
||||||
>;
|
>;
|
||||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">;
|
|
||||||
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "create" | "transaction">;
|
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "create" | "transaction">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
tokenService: Pick<TAuthTokenServiceFactory, "createTokenForUser">;
|
|
||||||
smtpService: Pick<TSmtpService, "sendMail">;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TSamlConfigServiceFactory = ReturnType<typeof samlConfigServiceFactory>;
|
export type TSamlConfigServiceFactory = ReturnType<typeof samlConfigServiceFactory>;
|
||||||
@@ -61,13 +48,9 @@ export const samlConfigServiceFactory = ({
|
|||||||
samlConfigDAL,
|
samlConfigDAL,
|
||||||
orgBotDAL,
|
orgBotDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
userDAL,
|
userDAL,
|
||||||
userAliasDAL,
|
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService
|
||||||
tokenService,
|
|
||||||
smtpService
|
|
||||||
}: TSamlConfigServiceFactoryDep) => {
|
}: TSamlConfigServiceFactoryDep) => {
|
||||||
const createSamlCfg = async ({
|
const createSamlCfg = async ({
|
||||||
cert,
|
cert,
|
||||||
@@ -322,7 +305,7 @@ export const samlConfigServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const samlLogin = async ({
|
const samlLogin = async ({
|
||||||
externalId,
|
username,
|
||||||
email,
|
email,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
@@ -331,40 +314,38 @@ export const samlConfigServiceFactory = ({
|
|||||||
relayState
|
relayState
|
||||||
}: TSamlLoginDTO) => {
|
}: TSamlLoginDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const serverCfg = await getServerCfg();
|
let user = await userDAL.findOne({ username });
|
||||||
const userAlias = await userAliasDAL.findOne({
|
|
||||||
externalId,
|
|
||||||
orgId,
|
|
||||||
aliasType: UserAliasType.SAML
|
|
||||||
});
|
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) throw new BadRequestError({ message: "Org not found" });
|
if (!organization) throw new BadRequestError({ message: "Org not found" });
|
||||||
|
|
||||||
let user: TUsers;
|
// TODO(dangtony98): remove this after aliases update
|
||||||
if (userAlias) {
|
if (authProvider === AuthMethod.KEYCLOAK_SAML && appCfg.LICENSE_SERVER_KEY) {
|
||||||
user = await userDAL.transaction(async (tx) => {
|
throw new BadRequestError({ message: "Keycloak SAML is not yet available on Infisical Cloud" });
|
||||||
const foundUser = await userDAL.findById(userAlias.userId, tx);
|
}
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
await userDAL.transaction(async (tx) => {
|
||||||
const [orgMembership] = await orgDAL.findMembership(
|
const [orgMembership] = await orgDAL.findMembership(
|
||||||
{
|
{
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: foundUser.id,
|
userId: user.id,
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
);
|
);
|
||||||
if (!orgMembership) {
|
if (!orgMembership) {
|
||||||
await orgMembershipDAL.create(
|
await orgDAL.createMembership(
|
||||||
{
|
{
|
||||||
userId: userAlias.userId,
|
userId: user.id,
|
||||||
inviteEmail: email,
|
|
||||||
orgId,
|
orgId,
|
||||||
|
inviteEmail: email,
|
||||||
role: OrgMembershipRole.Member,
|
role: OrgMembershipRole.Member,
|
||||||
status: foundUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
|
status: user.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
// Only update the membership to Accepted if the user account is already completed.
|
// Only update the membership to Accepted if the user account is already completed.
|
||||||
} else if (orgMembership.status === OrgMembershipStatus.Invited && foundUser.isAccepted) {
|
} else if (orgMembership.status === OrgMembershipStatus.Invited && user.isAccepted) {
|
||||||
await orgDAL.updateMembershipById(
|
await orgDAL.updateMembershipById(
|
||||||
orgMembership.id,
|
orgMembership.id,
|
||||||
{
|
{
|
||||||
@@ -373,97 +354,40 @@ export const samlConfigServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return foundUser;
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
user = await userDAL.transaction(async (tx) => {
|
user = await userDAL.transaction(async (tx) => {
|
||||||
let newUser: TUsers | undefined;
|
const newUser = await userDAL.create(
|
||||||
if (serverCfg.trustSamlEmails) {
|
|
||||||
newUser = await userDAL.findOne(
|
|
||||||
{
|
|
||||||
email,
|
|
||||||
isEmailVerified: true
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!newUser) {
|
|
||||||
const uniqueUsername = await normalizeUsername(`${firstName ?? ""}-${lastName ?? ""}`, userDAL);
|
|
||||||
newUser = await userDAL.create(
|
|
||||||
{
|
|
||||||
username: serverCfg.trustSamlEmails ? email : uniqueUsername,
|
|
||||||
email,
|
|
||||||
isEmailVerified: serverCfg.trustSamlEmails,
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
authMethods: [],
|
|
||||||
isGhost: false
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await userAliasDAL.create(
|
|
||||||
{
|
{
|
||||||
userId: newUser.id,
|
username,
|
||||||
aliasType: UserAliasType.SAML,
|
email,
|
||||||
externalId,
|
firstName,
|
||||||
emails: email ? [email] : [],
|
lastName,
|
||||||
orgId
|
authMethods: [AuthMethod.EMAIL],
|
||||||
|
isGhost: false
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
await orgDAL.createMembership({
|
||||||
const [orgMembership] = await orgDAL.findMembership(
|
inviteEmail: email,
|
||||||
{
|
orgId,
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: newUser.id,
|
role: OrgMembershipRole.Member,
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
status: OrgMembershipStatus.Invited
|
||||||
},
|
});
|
||||||
{ tx }
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!orgMembership) {
|
|
||||||
await orgMembershipDAL.create(
|
|
||||||
{
|
|
||||||
userId: newUser.id,
|
|
||||||
inviteEmail: email,
|
|
||||||
orgId,
|
|
||||||
role: OrgMembershipRole.Member,
|
|
||||||
status: newUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
// Only update the membership to Accepted if the user account is already completed.
|
|
||||||
} else if (orgMembership.status === OrgMembershipStatus.Invited && newUser.isAccepted) {
|
|
||||||
await orgDAL.updateMembershipById(
|
|
||||||
orgMembership.id,
|
|
||||||
{
|
|
||||||
status: OrgMembershipStatus.Accepted
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newUser;
|
return newUser;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const isUserCompleted = Boolean(user.isAccepted);
|
const isUserCompleted = Boolean(user.isAccepted);
|
||||||
const providerAuthToken = jwt.sign(
|
const providerAuthToken = jwt.sign(
|
||||||
{
|
{
|
||||||
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
...(user.email && { email: user.email, isEmailVerified: user.isEmailVerified }),
|
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
organizationName: organization.name,
|
organizationName: organization.name,
|
||||||
organizationId: organization.id,
|
organizationId: organization.id,
|
||||||
organizationSlug: organization.slug,
|
|
||||||
authMethod: authProvider,
|
authMethod: authProvider,
|
||||||
authType: UserAliasType.SAML,
|
|
||||||
isUserCompleted,
|
isUserCompleted,
|
||||||
...(relayState
|
...(relayState
|
||||||
? {
|
? {
|
||||||
@@ -479,22 +403,6 @@ export const samlConfigServiceFactory = ({
|
|||||||
|
|
||||||
await samlConfigDAL.update({ orgId }, { lastUsed: new Date() });
|
await samlConfigDAL.update({ orgId }, { lastUsed: new Date() });
|
||||||
|
|
||||||
if (user.email && !user.isEmailVerified) {
|
|
||||||
const token = await tokenService.createTokenForUser({
|
|
||||||
type: TokenType.TOKEN_EMAIL_VERIFICATION,
|
|
||||||
userId: user.id
|
|
||||||
});
|
|
||||||
|
|
||||||
await smtpService.sendMail({
|
|
||||||
template: SmtpTemplates.EmailVerification,
|
|
||||||
subjectLine: "Infisical confirmation code",
|
|
||||||
recipients: [user.email],
|
|
||||||
substitutions: {
|
|
||||||
code: token
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return { isUserCompleted, providerAuthToken };
|
return { isUserCompleted, providerAuthToken };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -45,8 +45,8 @@ export type TGetSamlCfgDTO =
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TSamlLoginDTO = {
|
export type TSamlLoginDTO = {
|
||||||
externalId: string;
|
username: string;
|
||||||
email: string;
|
email?: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName?: string;
|
lastName?: string;
|
||||||
authProvider: string;
|
authProvider: string;
|
||||||
|
@@ -2,31 +2,31 @@ import { TListScimGroups, TListScimUsers, TScimGroup, TScimUser } from "./scim-t
|
|||||||
|
|
||||||
export const buildScimUserList = ({
|
export const buildScimUserList = ({
|
||||||
scimUsers,
|
scimUsers,
|
||||||
startIndex,
|
offset,
|
||||||
limit
|
limit
|
||||||
}: {
|
}: {
|
||||||
scimUsers: TScimUser[];
|
scimUsers: TScimUser[];
|
||||||
startIndex: number;
|
offset: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
}): TListScimUsers => {
|
}): TListScimUsers => {
|
||||||
return {
|
return {
|
||||||
Resources: scimUsers,
|
Resources: scimUsers,
|
||||||
itemsPerPage: limit,
|
itemsPerPage: limit,
|
||||||
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
|
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
|
||||||
startIndex,
|
startIndex: offset,
|
||||||
totalResults: scimUsers.length
|
totalResults: scimUsers.length
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const buildScimUser = ({
|
export const buildScimUser = ({
|
||||||
orgMembershipId,
|
userId,
|
||||||
username,
|
username,
|
||||||
email,
|
email,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
active
|
active
|
||||||
}: {
|
}: {
|
||||||
orgMembershipId: string;
|
userId: string;
|
||||||
username: string;
|
username: string;
|
||||||
email?: string | null;
|
email?: string | null;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
@@ -35,7 +35,7 @@ export const buildScimUser = ({
|
|||||||
}): TScimUser => {
|
}): TScimUser => {
|
||||||
const scimUser = {
|
const scimUser = {
|
||||||
schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
||||||
id: orgMembershipId,
|
id: userId,
|
||||||
userName: username,
|
userName: username,
|
||||||
displayName: `${firstName} ${lastName}`,
|
displayName: `${firstName} ${lastName}`,
|
||||||
name: {
|
name: {
|
||||||
@@ -65,18 +65,18 @@ export const buildScimUser = ({
|
|||||||
|
|
||||||
export const buildScimGroupList = ({
|
export const buildScimGroupList = ({
|
||||||
scimGroups,
|
scimGroups,
|
||||||
startIndex,
|
offset,
|
||||||
limit
|
limit
|
||||||
}: {
|
}: {
|
||||||
scimGroups: TScimGroup[];
|
scimGroups: TScimGroup[];
|
||||||
startIndex: number;
|
offset: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
}): TListScimGroups => {
|
}): TListScimGroups => {
|
||||||
return {
|
return {
|
||||||
Resources: scimGroups,
|
Resources: scimGroups,
|
||||||
itemsPerPage: limit,
|
itemsPerPage: limit,
|
||||||
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
|
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
|
||||||
startIndex,
|
startIndex: offset,
|
||||||
totalResults: scimGroups.length
|
totalResults: scimGroups.length
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -2,7 +2,7 @@ import { ForbiddenError } from "@casl/ability";
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
|
|
||||||
import { OrgMembershipRole, OrgMembershipStatus, TableName, TGroups, TOrgMemberships, TUsers } from "@app/db/schemas";
|
import { OrgMembershipRole, OrgMembershipStatus, TableName, TGroups } from "@app/db/schemas";
|
||||||
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
||||||
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
|
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
|
||||||
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
||||||
@@ -11,25 +11,23 @@ import { getConfig } from "@app/lib/config/env";
|
|||||||
import { BadRequestError, ScimRequestError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, ScimRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { TOrgPermission } from "@app/lib/types";
|
import { TOrgPermission } from "@app/lib/types";
|
||||||
import { AuthTokenType } from "@app/services/auth/auth-type";
|
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { deleteOrgMembershipFn } from "@app/services/org/org-fns";
|
import { deleteOrgMembership } from "@app/services/org/org-fns";
|
||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
||||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
import { normalizeUsername } from "@app/services/user/user-fns";
|
|
||||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
|
||||||
import { UserAliasType } from "@app/services/user-alias/user-alias-types";
|
|
||||||
|
|
||||||
|
import { TAccessApprovalRequestDALFactory } from "../access-approval-request/access-approval-request-dal";
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
|
import { TSecretApprovalPolicyDALFactory } from "../secret-approval-policy/secret-approval-policy-dal";
|
||||||
|
import { TSecretApprovalRequestDALFactory } from "../secret-approval-request/secret-approval-request-dal";
|
||||||
import { buildScimGroup, buildScimGroupList, buildScimUser, buildScimUserList } from "./scim-fns";
|
import { buildScimGroup, buildScimGroupList, buildScimUser, buildScimUserList } from "./scim-fns";
|
||||||
import {
|
import {
|
||||||
TCreateScimGroupDTO,
|
TCreateScimGroupDTO,
|
||||||
@@ -52,32 +50,27 @@ import {
|
|||||||
|
|
||||||
type TScimServiceFactoryDep = {
|
type TScimServiceFactoryDep = {
|
||||||
scimDAL: Pick<TScimDALFactory, "create" | "find" | "findById" | "deleteById">;
|
scimDAL: Pick<TScimDALFactory, "create" | "find" | "findById" | "deleteById">;
|
||||||
userDAL: Pick<
|
userDAL: Pick<TUserDALFactory, "find" | "findOne" | "create" | "transaction" | "findUserEncKeyByUserIdsBatch">;
|
||||||
TUserDALFactory,
|
|
||||||
"find" | "findOne" | "create" | "transaction" | "findUserEncKeyByUserIdsBatch" | "findById"
|
|
||||||
>;
|
|
||||||
userAliasDAL: Pick<TUserAliasDALFactory, "findOne" | "create" | "delete">;
|
|
||||||
orgDAL: Pick<
|
orgDAL: Pick<
|
||||||
TOrgDALFactory,
|
TOrgDALFactory,
|
||||||
"createMembership" | "findById" | "findMembership" | "deleteMembershipById" | "transaction" | "updateMembershipById"
|
"createMembership" | "findById" | "findMembership" | "deleteMembershipById" | "transaction"
|
||||||
>;
|
>;
|
||||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "find" | "findOne" | "create" | "updateById">;
|
|
||||||
projectDAL: Pick<TProjectDALFactory, "find" | "findProjectGhostUser">;
|
projectDAL: Pick<TProjectDALFactory, "find" | "findProjectGhostUser">;
|
||||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find" | "delete" | "findProjectMembershipsByUserId">;
|
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find" | "delete">;
|
||||||
groupDAL: Pick<
|
groupDAL: Pick<
|
||||||
TGroupDALFactory,
|
TGroupDALFactory,
|
||||||
"create" | "findOne" | "findAllGroupMembers" | "update" | "delete" | "findGroups" | "transaction"
|
"create" | "findOne" | "findAllGroupMembers" | "update" | "delete" | "findGroups" | "transaction"
|
||||||
>;
|
>;
|
||||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
||||||
userGroupMembershipDAL: Pick<
|
userGroupMembershipDAL: TUserGroupMembershipDALFactory; // TODO: Pick
|
||||||
TUserGroupMembershipDALFactory,
|
|
||||||
"find" | "transaction" | "insertMany" | "filterProjectsByUserMembership" | "delete"
|
|
||||||
>;
|
|
||||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany" | "delete">;
|
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany" | "delete">;
|
||||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
smtpService: Pick<TSmtpService, "sendMail">;
|
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "delete">;
|
||||||
|
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "delete">;
|
||||||
|
secretApprovalPolicyDAL: Pick<TSecretApprovalPolicyDALFactory, "findByProjectIds">;
|
||||||
|
smtpService: TSmtpService;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TScimServiceFactory = ReturnType<typeof scimServiceFactory>;
|
export type TScimServiceFactory = ReturnType<typeof scimServiceFactory>;
|
||||||
@@ -86,9 +79,7 @@ export const scimServiceFactory = ({
|
|||||||
licenseService,
|
licenseService,
|
||||||
scimDAL,
|
scimDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
userAliasDAL,
|
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectMembershipDAL,
|
projectMembershipDAL,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
@@ -96,6 +87,9 @@ export const scimServiceFactory = ({
|
|||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
|
accessApprovalRequestDAL,
|
||||||
|
secretApprovalRequestDAL,
|
||||||
|
secretApprovalPolicyDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
smtpService
|
smtpService
|
||||||
}: TScimServiceFactoryDep) => {
|
}: TScimServiceFactoryDep) => {
|
||||||
@@ -175,7 +169,7 @@ export const scimServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// SCIM server endpoints
|
// SCIM server endpoints
|
||||||
const listScimUsers = async ({ startIndex, limit, filter, orgId }: TListScimUsersDTO): Promise<TListScimUsers> => {
|
const listScimUsers = async ({ offset, limit, filter, orgId }: TListScimUsersDTO): Promise<TListScimUsers> => {
|
||||||
const org = await orgDAL.findById(orgId);
|
const org = await orgDAL.findById(orgId);
|
||||||
|
|
||||||
if (!org.scimEnabled)
|
if (!org.scimEnabled)
|
||||||
@@ -193,11 +187,11 @@ export const scimServiceFactory = ({
|
|||||||
attributeName = "email";
|
attributeName = "email";
|
||||||
}
|
}
|
||||||
|
|
||||||
return { [attributeName]: parsedValue.replace(/"/g, "") };
|
return { [attributeName]: parsedValue };
|
||||||
};
|
};
|
||||||
|
|
||||||
const findOpts = {
|
const findOpts = {
|
||||||
...(startIndex && { offset: startIndex - 1 }),
|
...(offset && { offset }),
|
||||||
...(limit && { limit })
|
...(limit && { limit })
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -209,10 +203,10 @@ export const scimServiceFactory = ({
|
|||||||
findOpts
|
findOpts
|
||||||
);
|
);
|
||||||
|
|
||||||
const scimUsers = users.map(({ id, externalId, username, firstName, lastName, email }) =>
|
const scimUsers = users.map(({ userId, username, firstName, lastName, email }) =>
|
||||||
buildScimUser({
|
buildScimUser({
|
||||||
orgMembershipId: id ?? "",
|
userId: userId ?? "",
|
||||||
username: externalId ?? username,
|
username,
|
||||||
firstName: firstName ?? "",
|
firstName: firstName ?? "",
|
||||||
lastName: lastName ?? "",
|
lastName: lastName ?? "",
|
||||||
email,
|
email,
|
||||||
@@ -222,16 +216,16 @@ export const scimServiceFactory = ({
|
|||||||
|
|
||||||
return buildScimUserList({
|
return buildScimUserList({
|
||||||
scimUsers,
|
scimUsers,
|
||||||
startIndex,
|
offset,
|
||||||
limit
|
limit
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getScimUser = async ({ orgMembershipId, orgId }: TGetScimUserDTO) => {
|
const getScimUser = async ({ userId, orgId }: TGetScimUserDTO) => {
|
||||||
const [membership] = await orgDAL
|
const [membership] = await orgDAL
|
||||||
.findMembership({
|
.findMembership({
|
||||||
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
|
userId,
|
||||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
|
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
throw new ScimRequestError({
|
throw new ScimRequestError({
|
||||||
@@ -253,8 +247,8 @@ export const scimServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return buildScimUser({
|
return buildScimUser({
|
||||||
orgMembershipId: membership.id,
|
userId: membership.userId as string,
|
||||||
username: membership.externalId ?? membership.username,
|
username: membership.username,
|
||||||
email: membership.email ?? "",
|
email: membership.email ?? "",
|
||||||
firstName: membership.firstName as string,
|
firstName: membership.firstName as string,
|
||||||
lastName: membership.lastName as string,
|
lastName: membership.lastName as string,
|
||||||
@@ -262,9 +256,7 @@ export const scimServiceFactory = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const createScimUser = async ({ externalId, email, firstName, lastName, orgId }: TCreateScimUserDTO) => {
|
const createScimUser = async ({ username, email, firstName, lastName, orgId }: TCreateScimUserDTO) => {
|
||||||
if (!email) throw new ScimRequestError({ detail: "Invalid request. Missing email.", status: 400 });
|
|
||||||
|
|
||||||
const org = await orgDAL.findById(orgId);
|
const org = await orgDAL.findById(orgId);
|
||||||
|
|
||||||
if (!org)
|
if (!org)
|
||||||
@@ -279,121 +271,67 @@ export const scimServiceFactory = ({
|
|||||||
status: 403
|
status: 403
|
||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
let user = await userDAL.findOne({
|
||||||
const serverCfg = await getServerCfg();
|
username
|
||||||
|
|
||||||
const userAlias = await userAliasDAL.findOne({
|
|
||||||
externalId,
|
|
||||||
orgId,
|
|
||||||
aliasType: UserAliasType.SAML
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { user: createdUser, orgMembership: createdOrgMembership } = await userDAL.transaction(async (tx) => {
|
if (user) {
|
||||||
let user: TUsers | undefined;
|
await userDAL.transaction(async (tx) => {
|
||||||
let orgMembership: TOrgMemberships;
|
const [orgMembership] = await orgDAL.findMembership(
|
||||||
if (userAlias) {
|
|
||||||
user = await userDAL.findById(userAlias.userId, tx);
|
|
||||||
orgMembership = await orgMembershipDAL.findOne(
|
|
||||||
{
|
{
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
orgId
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!orgMembership) {
|
|
||||||
orgMembership = await orgMembershipDAL.create(
|
|
||||||
{
|
|
||||||
userId: userAlias.userId,
|
|
||||||
inviteEmail: email,
|
|
||||||
orgId,
|
|
||||||
role: OrgMembershipRole.Member,
|
|
||||||
status: user.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
} else if (orgMembership.status === OrgMembershipStatus.Invited && user.isAccepted) {
|
|
||||||
orgMembership = await orgMembershipDAL.updateById(
|
|
||||||
orgMembership.id,
|
|
||||||
{
|
|
||||||
status: OrgMembershipStatus.Accepted
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (serverCfg.trustSamlEmails) {
|
|
||||||
user = await userDAL.findOne(
|
|
||||||
{
|
|
||||||
email,
|
|
||||||
isEmailVerified: true
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
const uniqueUsername = await normalizeUsername(`${firstName}-${lastName}`, userDAL);
|
|
||||||
user = await userDAL.create(
|
|
||||||
{
|
|
||||||
username: serverCfg.trustSamlEmails ? email : uniqueUsername,
|
|
||||||
email,
|
|
||||||
isEmailVerified: serverCfg.trustSamlEmails,
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
authMethods: [],
|
|
||||||
isGhost: false
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await userAliasDAL.create(
|
|
||||||
{
|
|
||||||
userId: user.id,
|
|
||||||
aliasType: UserAliasType.SAML,
|
|
||||||
externalId,
|
|
||||||
emails: email ? [email] : [],
|
|
||||||
orgId
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
|
|
||||||
const [foundOrgMembership] = await orgDAL.findMembership(
|
|
||||||
{
|
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: user.id,
|
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
);
|
);
|
||||||
|
if (orgMembership)
|
||||||
orgMembership = foundOrgMembership;
|
throw new ScimRequestError({
|
||||||
|
detail: "User already exists in the database",
|
||||||
|
status: 409
|
||||||
|
});
|
||||||
|
|
||||||
if (!orgMembership) {
|
if (!orgMembership) {
|
||||||
orgMembership = await orgMembershipDAL.create(
|
await orgDAL.createMembership(
|
||||||
{
|
{
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
inviteEmail: email,
|
|
||||||
orgId,
|
orgId,
|
||||||
|
inviteEmail: email,
|
||||||
role: OrgMembershipRole.Member,
|
role: OrgMembershipRole.Member,
|
||||||
status: user.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
|
status: OrgMembershipStatus.Invited
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
// Only update the membership to Accepted if the user account is already completed.
|
|
||||||
} else if (orgMembership.status === OrgMembershipStatus.Invited && user.isAccepted) {
|
|
||||||
orgMembership = await orgDAL.updateMembershipById(
|
|
||||||
orgMembership.id,
|
|
||||||
{
|
|
||||||
status: OrgMembershipStatus.Accepted
|
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
} else {
|
||||||
|
user = await userDAL.transaction(async (tx) => {
|
||||||
|
const newUser = await userDAL.create(
|
||||||
|
{
|
||||||
|
username,
|
||||||
|
email,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
authMethods: [AuthMethod.EMAIL],
|
||||||
|
isGhost: false
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
return { user, orgMembership };
|
await orgDAL.createMembership(
|
||||||
});
|
{
|
||||||
|
inviteEmail: email,
|
||||||
|
orgId,
|
||||||
|
userId: newUser.id,
|
||||||
|
role: OrgMembershipRole.Member,
|
||||||
|
status: OrgMembershipStatus.Invited
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
return newUser;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const appCfg = getConfig();
|
||||||
|
|
||||||
if (email) {
|
if (email) {
|
||||||
await smtpService.sendMail({
|
await smtpService.sendMail({
|
||||||
@@ -408,20 +346,20 @@ export const scimServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return buildScimUser({
|
return buildScimUser({
|
||||||
orgMembershipId: createdOrgMembership.id,
|
userId: user.id,
|
||||||
username: externalId,
|
username: user.username,
|
||||||
firstName: createdUser.firstName as string,
|
firstName: user.firstName as string,
|
||||||
lastName: createdUser.lastName as string,
|
lastName: user.lastName as string,
|
||||||
email: createdUser.email ?? "",
|
email: user.email ?? "",
|
||||||
active: true
|
active: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateScimUser = async ({ orgMembershipId, orgId, operations }: TUpdateScimUserDTO) => {
|
const updateScimUser = async ({ userId, orgId, operations }: TUpdateScimUserDTO) => {
|
||||||
const [membership] = await orgDAL
|
const [membership] = await orgDAL
|
||||||
.findMembership({
|
.findMembership({
|
||||||
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
|
userId,
|
||||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
|
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
throw new ScimRequestError({
|
throw new ScimRequestError({
|
||||||
@@ -457,20 +395,18 @@ export const scimServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!active) {
|
if (!active) {
|
||||||
await deleteOrgMembershipFn({
|
await deleteOrgMembership({
|
||||||
orgMembershipId: membership.id,
|
orgMembershipId: membership.id,
|
||||||
orgId: membership.orgId,
|
orgId: membership.orgId,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
projectMembershipDAL,
|
projectDAL,
|
||||||
projectKeyDAL,
|
projectMembershipDAL
|
||||||
userAliasDAL,
|
|
||||||
licenseService
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildScimUser({
|
return buildScimUser({
|
||||||
orgMembershipId: membership.id,
|
userId: membership.userId as string,
|
||||||
username: membership.externalId ?? membership.username,
|
username: membership.username,
|
||||||
email: membership.email,
|
email: membership.email,
|
||||||
firstName: membership.firstName as string,
|
firstName: membership.firstName as string,
|
||||||
lastName: membership.lastName as string,
|
lastName: membership.lastName as string,
|
||||||
@@ -478,11 +414,11 @@ export const scimServiceFactory = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const replaceScimUser = async ({ orgMembershipId, active, orgId }: TReplaceScimUserDTO) => {
|
const replaceScimUser = async ({ userId, active, orgId }: TReplaceScimUserDTO) => {
|
||||||
const [membership] = await orgDAL
|
const [membership] = await orgDAL
|
||||||
.findMembership({
|
.findMembership({
|
||||||
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
|
userId,
|
||||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
|
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
throw new ScimRequestError({
|
throw new ScimRequestError({
|
||||||
@@ -504,20 +440,19 @@ export const scimServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!active) {
|
if (!active) {
|
||||||
await deleteOrgMembershipFn({
|
// tx
|
||||||
|
await deleteOrgMembership({
|
||||||
orgMembershipId: membership.id,
|
orgMembershipId: membership.id,
|
||||||
orgId: membership.orgId,
|
orgId: membership.orgId,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
projectMembershipDAL,
|
projectDAL,
|
||||||
projectKeyDAL,
|
projectMembershipDAL
|
||||||
userAliasDAL,
|
|
||||||
licenseService
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildScimUser({
|
return buildScimUser({
|
||||||
orgMembershipId: membership.id,
|
userId: membership.userId as string,
|
||||||
username: membership.externalId ?? membership.username,
|
username: membership.username,
|
||||||
email: membership.email,
|
email: membership.email,
|
||||||
firstName: membership.firstName as string,
|
firstName: membership.firstName as string,
|
||||||
lastName: membership.lastName as string,
|
lastName: membership.lastName as string,
|
||||||
@@ -525,11 +460,18 @@ export const scimServiceFactory = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteScimUser = async ({ orgMembershipId, orgId }: TDeleteScimUserDTO) => {
|
const deleteScimUser = async ({ userId, orgId }: TDeleteScimUserDTO) => {
|
||||||
const [membership] = await orgDAL.findMembership({
|
const [membership] = await orgDAL
|
||||||
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
|
.findMembership({
|
||||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
|
userId,
|
||||||
});
|
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
throw new ScimRequestError({
|
||||||
|
detail: "User not found",
|
||||||
|
status: 404
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
if (!membership)
|
if (!membership)
|
||||||
throw new ScimRequestError({
|
throw new ScimRequestError({
|
||||||
@@ -544,20 +486,18 @@ export const scimServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await deleteOrgMembershipFn({
|
await deleteOrgMembership({
|
||||||
orgMembershipId: membership.id,
|
orgMembershipId: membership.id,
|
||||||
orgId: membership.orgId,
|
orgId: membership.orgId,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
projectMembershipDAL,
|
projectDAL,
|
||||||
projectKeyDAL,
|
projectMembershipDAL
|
||||||
userAliasDAL,
|
|
||||||
licenseService
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {}; // intentionally return empty object upon success
|
return {}; // intentionally return empty object upon success
|
||||||
};
|
};
|
||||||
|
|
||||||
const listScimGroups = async ({ orgId, startIndex, limit }: TListScimGroupsDTO) => {
|
const listScimGroups = async ({ orgId, offset, limit }: TListScimGroupsDTO) => {
|
||||||
const plan = await licenseService.getPlan(orgId);
|
const plan = await licenseService.getPlan(orgId);
|
||||||
if (!plan.groups)
|
if (!plan.groups)
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -578,27 +518,21 @@ export const scimServiceFactory = ({
|
|||||||
status: 403
|
status: 403
|
||||||
});
|
});
|
||||||
|
|
||||||
const groups = await groupDAL.findGroups(
|
const groups = await groupDAL.findGroups({
|
||||||
{
|
orgId
|
||||||
orgId
|
});
|
||||||
},
|
|
||||||
{
|
|
||||||
offset: startIndex - 1,
|
|
||||||
limit
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const scimGroups = groups.map((group) =>
|
const scimGroups = groups.map((group) =>
|
||||||
buildScimGroup({
|
buildScimGroup({
|
||||||
groupId: group.id,
|
groupId: group.id,
|
||||||
name: group.name,
|
name: group.name,
|
||||||
members: [] // does this need to be populated?
|
members: []
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
return buildScimGroupList({
|
return buildScimGroupList({
|
||||||
scimGroups,
|
scimGroups,
|
||||||
startIndex,
|
offset,
|
||||||
limit
|
limit
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -637,15 +571,9 @@ export const scimServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (members && members.length) {
|
if (members && members.length) {
|
||||||
const orgMemberships = await orgMembershipDAL.find({
|
|
||||||
$in: {
|
|
||||||
id: members.map((member) => member.value)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const newMembers = await addUsersToGroupByUserIds({
|
const newMembers = await addUsersToGroupByUserIds({
|
||||||
group,
|
group,
|
||||||
userIds: orgMemberships.map((membership) => membership.userId as string),
|
userIds: members.map((member) => member.value),
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
@@ -662,19 +590,12 @@ export const scimServiceFactory = ({
|
|||||||
return { group, newMembers: [] };
|
return { group, newMembers: [] };
|
||||||
});
|
});
|
||||||
|
|
||||||
const orgMemberships = await orgDAL.findMembership({
|
|
||||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId,
|
|
||||||
$in: {
|
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: newGroup.newMembers.map((member) => member.id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return buildScimGroup({
|
return buildScimGroup({
|
||||||
groupId: newGroup.group.id,
|
groupId: newGroup.group.id,
|
||||||
name: newGroup.group.name,
|
name: newGroup.group.name,
|
||||||
members: orgMemberships.map(({ id, firstName, lastName }) => ({
|
members: newGroup.newMembers.map((member) => ({
|
||||||
value: id,
|
value: member.id,
|
||||||
display: `${firstName} ${lastName}`
|
display: `${member.firstName} ${member.lastName}`
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -703,22 +624,15 @@ export const scimServiceFactory = ({
|
|||||||
groupId: group.id
|
groupId: group.id
|
||||||
});
|
});
|
||||||
|
|
||||||
const orgMemberships = await orgDAL.findMembership({
|
|
||||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId,
|
|
||||||
$in: {
|
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: users
|
|
||||||
.filter((user) => user.isPartOfGroup)
|
|
||||||
.map((user) => user.id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return buildScimGroup({
|
return buildScimGroup({
|
||||||
groupId: group.id,
|
groupId: group.id,
|
||||||
name: group.name,
|
name: group.name,
|
||||||
members: orgMemberships.map(({ id, firstName, lastName }) => ({
|
members: users
|
||||||
value: id,
|
.filter((user) => user.isPartOfGroup)
|
||||||
display: `${firstName} ${lastName}`
|
.map((user) => ({
|
||||||
}))
|
value: user.id,
|
||||||
|
display: `${user.firstName} ${user.lastName}`
|
||||||
|
}))
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -762,13 +676,7 @@ export const scimServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (members) {
|
if (members) {
|
||||||
const orgMemberships = await orgMembershipDAL.find({
|
const membersIdsSet = new Set(members.map((member) => member.value));
|
||||||
$in: {
|
|
||||||
id: members.map((member) => member.value)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const membersIdsSet = new Set(orgMemberships.map((orgMembership) => orgMembership.userId));
|
|
||||||
|
|
||||||
const directMemberUserIds = (
|
const directMemberUserIds = (
|
||||||
await userGroupMembershipDAL.find({
|
await userGroupMembershipDAL.find({
|
||||||
@@ -787,13 +695,13 @@ export const scimServiceFactory = ({
|
|||||||
const allMembersUserIds = directMemberUserIds.concat(pendingGroupAdditionsUserIds);
|
const allMembersUserIds = directMemberUserIds.concat(pendingGroupAdditionsUserIds);
|
||||||
const allMembersUserIdsSet = new Set(allMembersUserIds);
|
const allMembersUserIdsSet = new Set(allMembersUserIds);
|
||||||
|
|
||||||
const toAddUserIds = orgMemberships.filter((member) => !allMembersUserIdsSet.has(member.userId as string));
|
const toAddUserIds = members.filter((member) => !allMembersUserIdsSet.has(member.value));
|
||||||
const toRemoveUserIds = allMembersUserIds.filter((userId) => !membersIdsSet.has(userId));
|
const toRemoveUserIds = allMembersUserIds.filter((userId) => !membersIdsSet.has(userId));
|
||||||
|
|
||||||
if (toAddUserIds.length) {
|
if (toAddUserIds.length) {
|
||||||
await addUsersToGroupByUserIds({
|
await addUsersToGroupByUserIds({
|
||||||
group,
|
group,
|
||||||
userIds: toAddUserIds.map((member) => member.userId as string),
|
userIds: toAddUserIds.map((member) => member.value),
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
@@ -811,6 +719,9 @@ export const scimServiceFactory = ({
|
|||||||
userIds: toRemoveUserIds,
|
userIds: toRemoveUserIds,
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
|
secretApprovalPolicyDAL,
|
||||||
|
accessApprovalRequestDAL,
|
||||||
|
secretApprovalRequestDAL,
|
||||||
groupProjectDAL,
|
groupProjectDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
tx
|
tx
|
||||||
|
@@ -12,7 +12,7 @@ export type TDeleteScimTokenDTO = {
|
|||||||
// SCIM server endpoint types
|
// SCIM server endpoint types
|
||||||
|
|
||||||
export type TListScimUsersDTO = {
|
export type TListScimUsersDTO = {
|
||||||
startIndex: number;
|
offset: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
filter?: string;
|
filter?: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
@@ -27,12 +27,12 @@ export type TListScimUsers = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TGetScimUserDTO = {
|
export type TGetScimUserDTO = {
|
||||||
orgMembershipId: string;
|
userId: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TCreateScimUserDTO = {
|
export type TCreateScimUserDTO = {
|
||||||
externalId: string;
|
username: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
@@ -40,7 +40,7 @@ export type TCreateScimUserDTO = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateScimUserDTO = {
|
export type TUpdateScimUserDTO = {
|
||||||
orgMembershipId: string;
|
userId: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
operations: {
|
operations: {
|
||||||
op: string;
|
op: string;
|
||||||
@@ -54,18 +54,18 @@ export type TUpdateScimUserDTO = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TReplaceScimUserDTO = {
|
export type TReplaceScimUserDTO = {
|
||||||
orgMembershipId: string;
|
userId: string;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeleteScimUserDTO = {
|
export type TDeleteScimUserDTO = {
|
||||||
orgMembershipId: string;
|
userId: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TListScimGroupsDTO = {
|
export type TListScimGroupsDTO = {
|
||||||
startIndex: number;
|
offset: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
@@ -20,7 +20,7 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretApprovalPolicy}.id`,
|
`${TableName.SecretApprovalPolicy}.id`,
|
||||||
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
||||||
)
|
)
|
||||||
.select(tx.ref("approverId").withSchema(TableName.SecretApprovalPolicyApprover))
|
.select(tx.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover))
|
||||||
.select(tx.ref("name").withSchema(TableName.Environment).as("envName"))
|
.select(tx.ref("name").withSchema(TableName.Environment).as("envName"))
|
||||||
.select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
.select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||||
.select(tx.ref("id").withSchema(TableName.Environment).as("envId"))
|
.select(tx.ref("id").withSchema(TableName.Environment).as("envId"))
|
||||||
@@ -33,18 +33,18 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
const doc = await sapFindQuery(tx || db, {
|
const doc = await sapFindQuery(tx || db, {
|
||||||
[`${TableName.SecretApprovalPolicy}.id` as "id"]: id
|
[`${TableName.SecretApprovalPolicy}.id` as "id"]: id
|
||||||
});
|
});
|
||||||
const formatedDoc = mergeOneToManyRelation(
|
const formattedDoc = mergeOneToManyRelation(
|
||||||
doc,
|
doc,
|
||||||
"id",
|
"id",
|
||||||
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
({ approverUserId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||||
...el,
|
...el,
|
||||||
envId,
|
envId,
|
||||||
environment: { id: envId, name, slug }
|
environment: { id: envId, name, slug }
|
||||||
}),
|
}),
|
||||||
({ approverId }) => approverId,
|
({ approverUserId }) => approverUserId,
|
||||||
"approvers"
|
"approvers"
|
||||||
);
|
);
|
||||||
return formatedDoc?.[0];
|
return formattedDoc?.[0];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "FindById" });
|
throw new DatabaseError({ error, name: "FindById" });
|
||||||
}
|
}
|
||||||
@@ -53,22 +53,31 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
const find = async (filter: TFindFilter<TSecretApprovalPolicies & { projectId: string }>, tx?: Knex) => {
|
const find = async (filter: TFindFilter<TSecretApprovalPolicies & { projectId: string }>, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const docs = await sapFindQuery(tx || db, filter);
|
const docs = await sapFindQuery(tx || db, filter);
|
||||||
const formatedDoc = mergeOneToManyRelation(
|
const formattedDoc = mergeOneToManyRelation(
|
||||||
docs,
|
docs,
|
||||||
"id",
|
"id",
|
||||||
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
({ approverUserId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||||
...el,
|
...el,
|
||||||
envId,
|
envId,
|
||||||
environment: { id: envId, name, slug }
|
environment: { id: envId, name, slug }
|
||||||
}),
|
}),
|
||||||
({ approverId }) => approverId,
|
({ approverUserId }) => approverUserId,
|
||||||
"approvers"
|
"approvers"
|
||||||
);
|
);
|
||||||
return formatedDoc;
|
return formattedDoc;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "Find" });
|
throw new DatabaseError({ error, name: "Find" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { ...secretApprovalPolicyOrm, findById, find };
|
const findByProjectIds = async (projectIds: string[], tx?: Knex) => {
|
||||||
|
const policies = await (tx || db)(TableName.SecretApprovalPolicy)
|
||||||
|
.join(TableName.Environment, `${TableName.SecretApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
||||||
|
.whereIn(`${TableName.Environment}.projectId`, projectIds)
|
||||||
|
.select(selectAllTableCols(TableName.SecretApprovalPolicy));
|
||||||
|
|
||||||
|
return policies;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...secretApprovalPolicyOrm, findById, find, findByProjectIds };
|
||||||
};
|
};
|
||||||
|
@@ -7,6 +7,7 @@ import { BadRequestError } from "@app/lib/errors";
|
|||||||
import { containsGlobPatterns } from "@app/lib/picomatch";
|
import { containsGlobPatterns } from "@app/lib/picomatch";
|
||||||
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
||||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||||
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
import { TSecretApprovalPolicyApproverDALFactory } from "./secret-approval-policy-approver-dal";
|
import { TSecretApprovalPolicyApproverDALFactory } from "./secret-approval-policy-approver-dal";
|
||||||
import { TSecretApprovalPolicyDALFactory } from "./secret-approval-policy-dal";
|
import { TSecretApprovalPolicyDALFactory } from "./secret-approval-policy-dal";
|
||||||
@@ -29,6 +30,7 @@ type TSecretApprovalPolicyServiceFactoryDep = {
|
|||||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
||||||
secretApprovalPolicyApproverDAL: TSecretApprovalPolicyApproverDALFactory;
|
secretApprovalPolicyApproverDAL: TSecretApprovalPolicyApproverDALFactory;
|
||||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
|
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
|
||||||
|
userDAL: Pick<TUserDALFactory, "findUsersByProjectId" | "findUserByProjectId">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TSecretApprovalPolicyServiceFactory = ReturnType<typeof secretApprovalPolicyServiceFactory>;
|
export type TSecretApprovalPolicyServiceFactory = ReturnType<typeof secretApprovalPolicyServiceFactory>;
|
||||||
@@ -38,7 +40,7 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
permissionService,
|
permissionService,
|
||||||
secretApprovalPolicyApproverDAL,
|
secretApprovalPolicyApproverDAL,
|
||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
projectMembershipDAL
|
userDAL
|
||||||
}: TSecretApprovalPolicyServiceFactoryDep) => {
|
}: TSecretApprovalPolicyServiceFactoryDep) => {
|
||||||
const createSecretApprovalPolicy = async ({
|
const createSecretApprovalPolicy = async ({
|
||||||
name,
|
name,
|
||||||
@@ -69,11 +71,12 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
const env = await projectEnvDAL.findOne({ slug: environment, projectId });
|
const env = await projectEnvDAL.findOne({ slug: environment, projectId });
|
||||||
if (!env) throw new BadRequestError({ message: "Environment not found" });
|
if (!env) throw new BadRequestError({ message: "Environment not found" });
|
||||||
|
|
||||||
const secretApprovers = await projectMembershipDAL.find({
|
const secretApproverUsers = await userDAL.findUsersByProjectId(
|
||||||
projectId,
|
projectId,
|
||||||
$in: { id: approvers }
|
approvers.map((approverUserId) => approverUserId)
|
||||||
});
|
);
|
||||||
if (secretApprovers.length !== approvers.length)
|
|
||||||
|
if (secretApproverUsers.length !== approvers.length)
|
||||||
throw new BadRequestError({ message: "Approver not found in project" });
|
throw new BadRequestError({ message: "Approver not found in project" });
|
||||||
|
|
||||||
const secretApproval = await secretApprovalPolicyDAL.transaction(async (tx) => {
|
const secretApproval = await secretApprovalPolicyDAL.transaction(async (tx) => {
|
||||||
@@ -87,8 +90,8 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await secretApprovalPolicyApproverDAL.insertMany(
|
await secretApprovalPolicyApproverDAL.insertMany(
|
||||||
secretApprovers.map(({ id }) => ({
|
secretApproverUsers.map(({ id }) => ({
|
||||||
approverId: id,
|
approverUserId: id,
|
||||||
policyId: doc.id
|
policyId: doc.id
|
||||||
})),
|
})),
|
||||||
tx
|
tx
|
||||||
@@ -132,21 +135,19 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
if (approvers) {
|
if (approvers) {
|
||||||
const secretApprovers = await projectMembershipDAL.find(
|
const secretApproverUsers = await userDAL.findUsersByProjectId(
|
||||||
{
|
secretApprovalPolicy.projectId,
|
||||||
projectId: secretApprovalPolicy.projectId,
|
approvers.map((approverUserId) => approverUserId)
|
||||||
$in: { id: approvers }
|
|
||||||
},
|
|
||||||
{ tx }
|
|
||||||
);
|
);
|
||||||
if (secretApprovers.length !== approvers.length)
|
|
||||||
|
if (secretApproverUsers.length !== approvers.length)
|
||||||
throw new BadRequestError({ message: "Approver not found in project" });
|
throw new BadRequestError({ message: "Approver not found in project" });
|
||||||
if (doc.approvals > secretApprovers.length)
|
if (doc.approvals > secretApproverUsers.length)
|
||||||
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
||||||
await secretApprovalPolicyApproverDAL.delete({ policyId: doc.id }, tx);
|
await secretApprovalPolicyApproverDAL.delete({ policyId: doc.id }, tx);
|
||||||
await secretApprovalPolicyApproverDAL.insertMany(
|
await secretApprovalPolicyApproverDAL.insertMany(
|
||||||
secretApprovers.map(({ id }) => ({
|
secretApproverUsers.map((user) => ({
|
||||||
approverId: id,
|
approverUserId: user.id,
|
||||||
policyId: doc.id
|
policyId: doc.id
|
||||||
})),
|
})),
|
||||||
tx
|
tx
|
||||||
|
@@ -16,7 +16,7 @@ export type TSecretApprovalRequestDALFactory = ReturnType<typeof secretApprovalR
|
|||||||
|
|
||||||
type TFindQueryFilter = {
|
type TFindQueryFilter = {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
membershipId: string;
|
actorId: string;
|
||||||
status?: RequestState;
|
status?: RequestState;
|
||||||
environment?: string;
|
environment?: string;
|
||||||
committer?: string;
|
committer?: string;
|
||||||
@@ -49,7 +49,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
)
|
)
|
||||||
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
||||||
.select(
|
.select(
|
||||||
tx.ref("member").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerMemberId"),
|
tx.ref("memberUserId").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerMemberId"),
|
||||||
tx.ref("status").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerStatus"),
|
tx.ref("status").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerStatus"),
|
||||||
tx.ref("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"),
|
tx.ref("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"),
|
||||||
tx.ref("name").withSchema(TableName.SecretApprovalPolicy).as("policyName"),
|
tx.ref("name").withSchema(TableName.SecretApprovalPolicy).as("policyName"),
|
||||||
@@ -57,14 +57,14 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
|
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
|
||||||
tx.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
tx.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
||||||
tx.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
|
tx.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
|
||||||
tx.ref("approverId").withSchema(TableName.SecretApprovalPolicyApprover)
|
tx.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover)
|
||||||
);
|
);
|
||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const sql = findQuery({ [`${TableName.SecretApprovalRequest}.id` as "id"]: id }, tx || db);
|
const sql = findQuery({ [`${TableName.SecretApprovalRequest}.id` as "id"]: id }, tx || db);
|
||||||
const docs = await sql;
|
const docs = await sql;
|
||||||
const formatedDoc = sqlNestRelationships({
|
const formattedDoc = sqlNestRelationships({
|
||||||
data: docs,
|
data: docs,
|
||||||
key: "id",
|
key: "id",
|
||||||
parentMapper: (el) => ({
|
parentMapper: (el) => ({
|
||||||
@@ -84,20 +84,20 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
label: "reviewers" as const,
|
label: "reviewers" as const,
|
||||||
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
|
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
|
||||||
},
|
},
|
||||||
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
|
{ key: "approverUserId", label: "approvers" as const, mapper: ({ approverUserId }) => approverUserId }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
if (!formatedDoc?.[0]) return;
|
if (!formattedDoc?.[0]) return;
|
||||||
return {
|
return {
|
||||||
...formatedDoc[0],
|
...formattedDoc[0],
|
||||||
policy: { ...formatedDoc[0].policy, approvers: formatedDoc[0].approvers }
|
policy: { ...formattedDoc[0].policy, approvers: formattedDoc[0].approvers }
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "FindByIdSAR" });
|
throw new DatabaseError({ error, name: "FindByIdSAR" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const findProjectRequestCount = async (projectId: string, membershipId: string, tx?: Knex) => {
|
const findProjectRequestCount = async (projectId: string, approverUserId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const docs = await (tx || db)
|
const docs = await (tx || db)
|
||||||
.with(
|
.with(
|
||||||
@@ -110,12 +110,12 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretApprovalRequest}.policyId`,
|
`${TableName.SecretApprovalRequest}.policyId`,
|
||||||
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
||||||
)
|
)
|
||||||
.where({ projectId })
|
.where({ [`${TableName.Environment}.projectId` as "projectId"]: projectId })
|
||||||
.andWhere(
|
.andWhere(
|
||||||
(bd) =>
|
(bd) =>
|
||||||
void bd
|
void bd
|
||||||
.where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId)
|
.where(`${TableName.SecretApprovalPolicyApprover}.approverUserId`, approverUserId)
|
||||||
.orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId)
|
.orWhere(`${TableName.SecretApprovalRequest}.committerUserId`, approverUserId)
|
||||||
)
|
)
|
||||||
.select("status", `${TableName.SecretApprovalRequest}.id`)
|
.select("status", `${TableName.SecretApprovalRequest}.id`)
|
||||||
.groupBy(`${TableName.SecretApprovalRequest}.id`, "status")
|
.groupBy(`${TableName.SecretApprovalRequest}.id`, "status")
|
||||||
@@ -142,7 +142,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const findByProjectId = async (
|
const findByProjectId = async (
|
||||||
{ status, limit = 20, offset = 0, projectId, committer, environment, membershipId }: TFindQueryFilter,
|
{ status, limit = 20, offset = 0, projectId, committer, environment, actorId }: TFindQueryFilter,
|
||||||
tx?: Knex
|
tx?: Knex
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
@@ -173,21 +173,21 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
)
|
)
|
||||||
.where(
|
.where(
|
||||||
stripUndefinedInWhere({
|
stripUndefinedInWhere({
|
||||||
projectId,
|
[`${TableName.Environment}.projectId`]: projectId,
|
||||||
[`${TableName.Environment}.slug` as "slug"]: environment,
|
[`${TableName.Environment}.slug` as "slug"]: environment,
|
||||||
[`${TableName.SecretApprovalRequest}.status`]: status,
|
[`${TableName.SecretApprovalRequest}.status`]: status,
|
||||||
committerId: committer
|
committerUserId: committer
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.andWhere(
|
.andWhere(
|
||||||
(bd) =>
|
(bd) =>
|
||||||
void bd
|
void bd
|
||||||
.where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId)
|
.where(`${TableName.SecretApprovalPolicyApprover}.approverUserId`, actorId)
|
||||||
.orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId)
|
.orWhere(`${TableName.SecretApprovalRequest}.committerUserId`, actorId)
|
||||||
)
|
)
|
||||||
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
||||||
.select(
|
.select(
|
||||||
db.ref("projectId").withSchema(TableName.Environment),
|
db.ref("projectId").withSchema(TableName.Environment).as("envProjectId"),
|
||||||
db.ref("slug").withSchema(TableName.Environment).as("environment"),
|
db.ref("slug").withSchema(TableName.Environment).as("environment"),
|
||||||
db.ref("id").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerMemberId"),
|
db.ref("id").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerMemberId"),
|
||||||
db.ref("status").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerStatus"),
|
db.ref("status").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerStatus"),
|
||||||
@@ -201,7 +201,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
),
|
),
|
||||||
db.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
db.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
||||||
db.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
|
db.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
|
||||||
db.ref("approverId").withSchema(TableName.SecretApprovalPolicyApprover)
|
db.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover)
|
||||||
)
|
)
|
||||||
.orderBy("createdAt", "desc");
|
.orderBy("createdAt", "desc");
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
parentMapper: (el) => ({
|
parentMapper: (el) => ({
|
||||||
...SecretApprovalRequestsSchema.parse(el),
|
...SecretApprovalRequestsSchema.parse(el),
|
||||||
environment: el.environment,
|
environment: el.environment,
|
||||||
projectId: el.projectId,
|
projectId: el.envProjectId,
|
||||||
policy: {
|
policy: {
|
||||||
id: el.policyId,
|
id: el.policyId,
|
||||||
name: el.policyName,
|
name: el.policyName,
|
||||||
@@ -232,9 +232,9 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
mapper: ({ reviewerMemberId: member, reviewerStatus: s }) => (member ? { member, status: s } : undefined)
|
mapper: ({ reviewerMemberId: member, reviewerStatus: s }) => (member ? { member, status: s } : undefined)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "approverId",
|
key: "approverUserId",
|
||||||
label: "approvers" as const,
|
label: "approvers" as const,
|
||||||
mapper: ({ approverId }) => approverId
|
mapper: ({ approverUserId }) => approverUserId
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "commitId",
|
key: "commitId",
|
||||||
|
@@ -113,7 +113,7 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("secretCommentTag").withSchema(TableName.SecretVersion).as("secVerCommentTag"),
|
db.ref("secretCommentTag").withSchema(TableName.SecretVersion).as("secVerCommentTag"),
|
||||||
db.ref("secretCommentCiphertext").withSchema(TableName.SecretVersion).as("secVerCommentCiphertext")
|
db.ref("secretCommentCiphertext").withSchema(TableName.SecretVersion).as("secVerCommentCiphertext")
|
||||||
);
|
);
|
||||||
const formatedDoc = sqlNestRelationships({
|
const formattedDoc = sqlNestRelationships({
|
||||||
data: doc,
|
data: doc,
|
||||||
key: "id",
|
key: "id",
|
||||||
parentMapper: (data) => SecretApprovalRequestsSecretsSchema.omit({ secretVersion: true }).parse(data),
|
parentMapper: (data) => SecretApprovalRequestsSecretsSchema.omit({ secretVersion: true }).parse(data),
|
||||||
@@ -212,7 +212,7 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
return formatedDoc?.map(({ secret, secretVersion, ...el }) => ({
|
return formattedDoc?.map(({ secret, secretVersion, ...el }) => ({
|
||||||
...el,
|
...el,
|
||||||
secret: secret?.[0],
|
secret: secret?.[0],
|
||||||
secretVersion: secretVersion?.[0]
|
secretVersion: secretVersion?.[0]
|
||||||
|
@@ -7,24 +7,14 @@ import {
|
|||||||
SecretType,
|
SecretType,
|
||||||
TSecretApprovalRequestsSecretsInsert
|
TSecretApprovalRequestsSecretsInsert
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
|
||||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { groupBy, pick, unique } from "@app/lib/fn";
|
import { groupBy, pick, unique } from "@app/lib/fn";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
|
||||||
import { TSecretDALFactory } from "@app/services/secret/secret-dal";
|
import { TSecretDALFactory } from "@app/services/secret/secret-dal";
|
||||||
import {
|
|
||||||
fnSecretBlindIndexCheck,
|
|
||||||
fnSecretBlindIndexCheckV2,
|
|
||||||
fnSecretBulkDelete,
|
|
||||||
fnSecretBulkInsert,
|
|
||||||
fnSecretBulkUpdate,
|
|
||||||
getAllNestedSecretReferences
|
|
||||||
} from "@app/services/secret/secret-fns";
|
|
||||||
import { TSecretQueueFactory } from "@app/services/secret/secret-queue";
|
import { TSecretQueueFactory } from "@app/services/secret/secret-queue";
|
||||||
import { SecretOperations } from "@app/services/secret/secret-types";
|
import { TSecretServiceFactory } from "@app/services/secret/secret-service";
|
||||||
import { TSecretVersionDALFactory } from "@app/services/secret/secret-version-dal";
|
import { TSecretVersionDALFactory } from "@app/services/secret/secret-version-dal";
|
||||||
import { TSecretVersionTagDALFactory } from "@app/services/secret/secret-version-tag-dal";
|
import { TSecretVersionTagDALFactory } from "@app/services/secret/secret-version-tag-dal";
|
||||||
import { TSecretBlindIndexDALFactory } from "@app/services/secret-blind-index/secret-blind-index-dal";
|
import { TSecretBlindIndexDALFactory } from "@app/services/secret-blind-index/secret-blind-index-dal";
|
||||||
@@ -39,6 +29,7 @@ import { TSecretApprovalRequestReviewerDALFactory } from "./secret-approval-requ
|
|||||||
import { TSecretApprovalRequestSecretDALFactory } from "./secret-approval-request-secret-dal";
|
import { TSecretApprovalRequestSecretDALFactory } from "./secret-approval-request-secret-dal";
|
||||||
import {
|
import {
|
||||||
ApprovalStatus,
|
ApprovalStatus,
|
||||||
|
CommitType,
|
||||||
RequestState,
|
RequestState,
|
||||||
TApprovalRequestCountDTO,
|
TApprovalRequestCountDTO,
|
||||||
TGenerateSecretApprovalRequestDTO,
|
TGenerateSecretApprovalRequestDTO,
|
||||||
@@ -51,11 +42,10 @@ import {
|
|||||||
|
|
||||||
type TSecretApprovalRequestServiceFactoryDep = {
|
type TSecretApprovalRequestServiceFactoryDep = {
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||||
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
|
|
||||||
secretApprovalRequestDAL: TSecretApprovalRequestDALFactory;
|
secretApprovalRequestDAL: TSecretApprovalRequestDALFactory;
|
||||||
secretApprovalRequestSecretDAL: TSecretApprovalRequestSecretDALFactory;
|
secretApprovalRequestSecretDAL: TSecretApprovalRequestSecretDALFactory;
|
||||||
secretApprovalRequestReviewerDAL: TSecretApprovalRequestReviewerDALFactory;
|
secretApprovalRequestReviewerDAL: TSecretApprovalRequestReviewerDALFactory;
|
||||||
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath" | "findSecretPathByFolderIds">;
|
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath" | "findById" | "findSecretPathByFolderIds">;
|
||||||
secretDAL: TSecretDALFactory;
|
secretDAL: TSecretDALFactory;
|
||||||
secretTagDAL: Pick<TSecretTagDALFactory, "findManyTagsById" | "saveTagsToSecret" | "deleteTagsManySecret">;
|
secretTagDAL: Pick<TSecretTagDALFactory, "findManyTagsById" | "saveTagsToSecret" | "deleteTagsManySecret">;
|
||||||
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
|
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
|
||||||
@@ -63,7 +53,15 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
|||||||
secretVersionDAL: Pick<TSecretVersionDALFactory, "findLatestVersionMany" | "insertMany">;
|
secretVersionDAL: Pick<TSecretVersionDALFactory, "findLatestVersionMany" | "insertMany">;
|
||||||
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
|
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus">;
|
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus">;
|
||||||
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "removeSecretReminder">;
|
secretService: Pick<
|
||||||
|
TSecretServiceFactory,
|
||||||
|
| "fnSecretBulkInsert"
|
||||||
|
| "fnSecretBulkUpdate"
|
||||||
|
| "fnSecretBlindIndexCheck"
|
||||||
|
| "fnSecretBulkDelete"
|
||||||
|
| "fnSecretBlindIndexCheckV2"
|
||||||
|
>;
|
||||||
|
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TSecretApprovalRequestServiceFactory = ReturnType<typeof secretApprovalRequestServiceFactory>;
|
export type TSecretApprovalRequestServiceFactory = ReturnType<typeof secretApprovalRequestServiceFactory>;
|
||||||
@@ -80,14 +78,14 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
projectDAL,
|
projectDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
snapshotService,
|
snapshotService,
|
||||||
|
secretService,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretQueueService,
|
secretQueueService
|
||||||
projectBotService
|
|
||||||
}: TSecretApprovalRequestServiceFactoryDep) => {
|
}: TSecretApprovalRequestServiceFactoryDep) => {
|
||||||
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
|
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
|
||||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||||
|
|
||||||
const { membership } = await permissionService.getProjectPermission(
|
await permissionService.getProjectPermission(
|
||||||
actor as ActorType.USER,
|
actor as ActorType.USER,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
@@ -95,7 +93,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
|
|
||||||
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, membership.id);
|
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, actorId);
|
||||||
return count;
|
return count;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -113,19 +111,14 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
}: TListApprovalsDTO) => {
|
}: TListApprovalsDTO) => {
|
||||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||||
|
|
||||||
const { membership } = await permissionService.getProjectPermission(
|
await permissionService.getProjectPermission(actor, actorId, projectId, actorAuthMethod, actorOrgId);
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
const approvals = await secretApprovalRequestDAL.findByProjectId({
|
const approvals = await secretApprovalRequestDAL.findByProjectId({
|
||||||
projectId,
|
projectId,
|
||||||
committer,
|
committer,
|
||||||
environment,
|
environment,
|
||||||
status,
|
status,
|
||||||
membershipId: membership.id,
|
actorId,
|
||||||
limit,
|
limit,
|
||||||
offset
|
offset
|
||||||
});
|
});
|
||||||
@@ -145,7 +138,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
|
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
|
||||||
|
|
||||||
const { policy } = secretApprovalRequest;
|
const { policy } = secretApprovalRequest;
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
const { hasRole } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
secretApprovalRequest.projectId,
|
secretApprovalRequest.projectId,
|
||||||
@@ -154,8 +147,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
secretApprovalRequest.committerId !== membership.id &&
|
secretApprovalRequest.committerUserId !== actorId &&
|
||||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
!policy.approvers.find((approverUserId) => approverUserId === actorId)
|
||||||
) {
|
) {
|
||||||
throw new UnauthorizedError({ message: "User has no access" });
|
throw new UnauthorizedError({ message: "User has no access" });
|
||||||
}
|
}
|
||||||
@@ -180,7 +173,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
||||||
|
|
||||||
const { policy } = secretApprovalRequest;
|
const { policy } = secretApprovalRequest;
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
const { hasRole } = await permissionService.getProjectPermission(
|
||||||
ActorType.USER,
|
ActorType.USER,
|
||||||
actorId,
|
actorId,
|
||||||
secretApprovalRequest.projectId,
|
secretApprovalRequest.projectId,
|
||||||
@@ -189,8 +182,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
secretApprovalRequest.committerId !== membership.id &&
|
secretApprovalRequest.committerUserId !== actorId &&
|
||||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
!policy.approvers.find((approverUserId) => approverUserId === actorId)
|
||||||
) {
|
) {
|
||||||
throw new UnauthorizedError({ message: "User has no access" });
|
throw new UnauthorizedError({ message: "User has no access" });
|
||||||
}
|
}
|
||||||
@@ -198,7 +191,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
const review = await secretApprovalRequestReviewerDAL.findOne(
|
const review = await secretApprovalRequestReviewerDAL.findOne(
|
||||||
{
|
{
|
||||||
requestId: secretApprovalRequest.id,
|
requestId: secretApprovalRequest.id,
|
||||||
member: membership.id
|
memberUserId: actorId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -207,7 +200,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
{
|
{
|
||||||
status,
|
status,
|
||||||
requestId: secretApprovalRequest.id,
|
requestId: secretApprovalRequest.id,
|
||||||
member: membership.id
|
memberUserId: actorId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -230,7 +223,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
||||||
|
|
||||||
const { policy } = secretApprovalRequest;
|
const { policy } = secretApprovalRequest;
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
const { hasRole } = await permissionService.getProjectPermission(
|
||||||
ActorType.USER,
|
ActorType.USER,
|
||||||
actorId,
|
actorId,
|
||||||
secretApprovalRequest.projectId,
|
secretApprovalRequest.projectId,
|
||||||
@@ -239,8 +232,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
secretApprovalRequest.committerId !== membership.id &&
|
secretApprovalRequest.committerUserId !== actorId &&
|
||||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
!policy.approvers.find((approverUserId) => approverUserId === actorId)
|
||||||
) {
|
) {
|
||||||
throw new UnauthorizedError({ message: "User has no access" });
|
throw new UnauthorizedError({ message: "User has no access" });
|
||||||
}
|
}
|
||||||
@@ -253,8 +246,9 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
|
|
||||||
const updatedRequest = await secretApprovalRequestDAL.updateById(secretApprovalRequest.id, {
|
const updatedRequest = await secretApprovalRequestDAL.updateById(secretApprovalRequest.id, {
|
||||||
status,
|
status,
|
||||||
statusChangeBy: membership.id
|
statusChangeByUserId: actorId
|
||||||
});
|
});
|
||||||
|
|
||||||
return { ...secretApprovalRequest, ...updatedRequest };
|
return { ...secretApprovalRequest, ...updatedRequest };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -270,7 +264,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
||||||
|
|
||||||
const { policy, folderId, projectId } = secretApprovalRequest;
|
const { policy, folderId, projectId } = secretApprovalRequest;
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
const { hasRole } = await permissionService.getProjectPermission(
|
||||||
ActorType.USER,
|
ActorType.USER,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
@@ -280,8 +274,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
secretApprovalRequest.committerId !== membership.id &&
|
secretApprovalRequest.committerUserId !== actorId &&
|
||||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
!policy.approvers.find((approverUserId) => approverUserId === actorId)
|
||||||
) {
|
) {
|
||||||
throw new UnauthorizedError({ message: "User has no access" });
|
throw new UnauthorizedError({ message: "User has no access" });
|
||||||
}
|
}
|
||||||
@@ -292,19 +286,18 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
const hasMinApproval =
|
const hasMinApproval =
|
||||||
secretApprovalRequest.policy.approvals <=
|
secretApprovalRequest.policy.approvals <=
|
||||||
secretApprovalRequest.policy.approvers.filter(
|
secretApprovalRequest.policy.approvers.filter(
|
||||||
(approverId) => reviewers[approverId.toString()] === ApprovalStatus.APPROVED
|
(approverUserId) => reviewers[approverUserId.toString()] === ApprovalStatus.APPROVED
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
if (!hasMinApproval) throw new BadRequestError({ message: "Doesn't have minimum approvals needed" });
|
if (!hasMinApproval) throw new BadRequestError({ message: "Doesn't have minimum approvals needed" });
|
||||||
const secretApprovalSecrets = await secretApprovalRequestSecretDAL.findByRequestId(secretApprovalRequest.id);
|
const secretApprovalSecrets = await secretApprovalRequestSecretDAL.findByRequestId(secretApprovalRequest.id);
|
||||||
if (!secretApprovalSecrets) throw new BadRequestError({ message: "No secrets found" });
|
if (!secretApprovalSecrets) throw new BadRequestError({ message: "No secrets found" });
|
||||||
|
|
||||||
const conflicts: Array<{ secretId: string; op: SecretOperations }> = [];
|
const conflicts: Array<{ secretId: string; op: CommitType }> = [];
|
||||||
let secretCreationCommits = secretApprovalSecrets.filter(({ op }) => op === SecretOperations.Create);
|
let secretCreationCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Create);
|
||||||
if (secretCreationCommits.length) {
|
if (secretCreationCommits.length) {
|
||||||
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await fnSecretBlindIndexCheckV2({
|
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await secretService.fnSecretBlindIndexCheckV2({
|
||||||
folderId,
|
folderId,
|
||||||
secretDAL,
|
|
||||||
inputSecrets: secretCreationCommits.map(({ secretBlindIndex }) => {
|
inputSecrets: secretCreationCommits.map(({ secretBlindIndex }) => {
|
||||||
if (!secretBlindIndex) {
|
if (!secretBlindIndex) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -317,19 +310,17 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
secretCreationCommits
|
secretCreationCommits
|
||||||
.filter(({ secretBlindIndex }) => conflictGroupByBlindIndex[secretBlindIndex || ""])
|
.filter(({ secretBlindIndex }) => conflictGroupByBlindIndex[secretBlindIndex || ""])
|
||||||
.forEach((el) => {
|
.forEach((el) => {
|
||||||
conflicts.push({ op: SecretOperations.Create, secretId: el.id });
|
conflicts.push({ op: CommitType.Create, secretId: el.id });
|
||||||
});
|
});
|
||||||
secretCreationCommits = secretCreationCommits.filter(
|
secretCreationCommits = secretCreationCommits.filter(
|
||||||
({ secretBlindIndex }) => !conflictGroupByBlindIndex[secretBlindIndex || ""]
|
({ secretBlindIndex }) => !conflictGroupByBlindIndex[secretBlindIndex || ""]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let secretUpdationCommits = secretApprovalSecrets.filter(({ op }) => op === SecretOperations.Update);
|
let secretUpdationCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Update);
|
||||||
if (secretUpdationCommits.length) {
|
if (secretUpdationCommits.length) {
|
||||||
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await fnSecretBlindIndexCheckV2({
|
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await secretService.fnSecretBlindIndexCheckV2({
|
||||||
folderId,
|
folderId,
|
||||||
secretDAL,
|
|
||||||
userId: "",
|
|
||||||
inputSecrets: secretUpdationCommits
|
inputSecrets: secretUpdationCommits
|
||||||
.filter(({ secretBlindIndex, secret }) => secret && secret.secretBlindIndex !== secretBlindIndex)
|
.filter(({ secretBlindIndex, secret }) => secret && secret.secretBlindIndex !== secretBlindIndex)
|
||||||
.map(({ secretBlindIndex }) => {
|
.map(({ secretBlindIndex }) => {
|
||||||
@@ -347,7 +338,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
(secretBlindIndex && conflictGroupByBlindIndex[secretBlindIndex]) || !secretId
|
(secretBlindIndex && conflictGroupByBlindIndex[secretBlindIndex]) || !secretId
|
||||||
)
|
)
|
||||||
.forEach((el) => {
|
.forEach((el) => {
|
||||||
conflicts.push({ op: SecretOperations.Update, secretId: el.id });
|
conflicts.push({ op: CommitType.Update, secretId: el.id });
|
||||||
});
|
});
|
||||||
|
|
||||||
secretUpdationCommits = secretUpdationCommits.filter(
|
secretUpdationCommits = secretUpdationCommits.filter(
|
||||||
@@ -356,11 +347,11 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const secretDeletionCommits = secretApprovalSecrets.filter(({ op }) => op === SecretOperations.Delete);
|
const secretDeletionCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Delete);
|
||||||
const botKey = await projectBotService.getBotKey(projectId).catch(() => null);
|
|
||||||
const mergeStatus = await secretApprovalRequestDAL.transaction(async (tx) => {
|
const mergeStatus = await secretApprovalRequestDAL.transaction(async (tx) => {
|
||||||
const newSecrets = secretCreationCommits.length
|
const newSecrets = secretCreationCommits.length
|
||||||
? await fnSecretBulkInsert({
|
? await secretService.fnSecretBulkInsert({
|
||||||
tx,
|
tx,
|
||||||
folderId,
|
folderId,
|
||||||
inputSecrets: secretCreationCommits.map((el) => ({
|
inputSecrets: secretCreationCommits.map((el) => ({
|
||||||
@@ -384,17 +375,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
]),
|
]),
|
||||||
tags: el?.tags.map(({ id }) => id),
|
tags: el?.tags.map(({ id }) => id),
|
||||||
version: 1,
|
version: 1,
|
||||||
type: SecretType.Shared,
|
type: SecretType.Shared
|
||||||
references: botKey
|
|
||||||
? getAllNestedSecretReferences(
|
|
||||||
decryptSymmetric128BitHexKeyUTF8({
|
|
||||||
ciphertext: el.secretValueCiphertext,
|
|
||||||
iv: el.secretValueIV,
|
|
||||||
tag: el.secretValueTag,
|
|
||||||
key: botKey
|
|
||||||
})
|
|
||||||
)
|
|
||||||
: undefined
|
|
||||||
})),
|
})),
|
||||||
secretDAL,
|
secretDAL,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
@@ -403,7 +384,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
const updatedSecrets = secretUpdationCommits.length
|
const updatedSecrets = secretUpdationCommits.length
|
||||||
? await fnSecretBulkUpdate({
|
? await secretService.fnSecretBulkUpdate({
|
||||||
folderId,
|
folderId,
|
||||||
projectId,
|
projectId,
|
||||||
tx,
|
tx,
|
||||||
@@ -429,17 +410,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
"secretReminderNote",
|
"secretReminderNote",
|
||||||
"secretReminderRepeatDays",
|
"secretReminderRepeatDays",
|
||||||
"secretBlindIndex"
|
"secretBlindIndex"
|
||||||
]),
|
])
|
||||||
references: botKey
|
|
||||||
? getAllNestedSecretReferences(
|
|
||||||
decryptSymmetric128BitHexKeyUTF8({
|
|
||||||
ciphertext: el.secretValueCiphertext,
|
|
||||||
iv: el.secretValueIV,
|
|
||||||
tag: el.secretValueTag,
|
|
||||||
key: botKey
|
|
||||||
})
|
|
||||||
)
|
|
||||||
: undefined
|
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
secretDAL,
|
secretDAL,
|
||||||
@@ -449,13 +420,11 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
const deletedSecret = secretDeletionCommits.length
|
const deletedSecret = secretDeletionCommits.length
|
||||||
? await fnSecretBulkDelete({
|
? await secretService.fnSecretBulkDelete({
|
||||||
projectId,
|
projectId,
|
||||||
folderId,
|
folderId,
|
||||||
tx,
|
tx,
|
||||||
actorId: "",
|
actorId: "",
|
||||||
secretDAL,
|
|
||||||
secretQueueService,
|
|
||||||
inputSecrets: secretDeletionCommits.map(({ secretBlindIndex }) => {
|
inputSecrets: secretDeletionCommits.map(({ secretBlindIndex }) => {
|
||||||
if (!secretBlindIndex) {
|
if (!secretBlindIndex) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -472,7 +441,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
conflicts: JSON.stringify(conflicts),
|
conflicts: JSON.stringify(conflicts),
|
||||||
hasMerged: true,
|
hasMerged: true,
|
||||||
status: RequestState.Closed,
|
status: RequestState.Closed,
|
||||||
statusChangeBy: membership.id
|
statusChangeByUserId: actorId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -482,14 +451,12 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
await snapshotService.performSnapshot(folderId);
|
await snapshotService.performSnapshot(folderId);
|
||||||
const [folder] = await folderDAL.findSecretPathByFolderIds(projectId, [folderId]);
|
const folder = await folderDAL.findById(folderId);
|
||||||
if (!folder) throw new BadRequestError({ message: "Folder not found" });
|
// TODO(akhilmhdh-pg): change query to do secret path from folder
|
||||||
await secretQueueService.syncSecrets({
|
await secretQueueService.syncSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
secretPath: folder.path,
|
secretPath: "/",
|
||||||
environmentSlug: folder.environmentSlug,
|
environment: folder?.environment.envSlug as string
|
||||||
actorId,
|
|
||||||
actor
|
|
||||||
});
|
});
|
||||||
return mergeStatus;
|
return mergeStatus;
|
||||||
};
|
};
|
||||||
@@ -509,7 +476,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
}: TGenerateSecretApprovalRequestDTO) => {
|
}: TGenerateSecretApprovalRequestDTO) => {
|
||||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||||
|
|
||||||
const { permission, membership } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
@@ -537,9 +504,9 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
const commits: Omit<TSecretApprovalRequestsSecretsInsert, "requestId">[] = [];
|
const commits: Omit<TSecretApprovalRequestsSecretsInsert, "requestId">[] = [];
|
||||||
const commitTagIds: Record<string, string[]> = {};
|
const commitTagIds: Record<string, string[]> = {};
|
||||||
// for created secret approval change
|
// for created secret approval change
|
||||||
const createdSecrets = data[SecretOperations.Create];
|
const createdSecrets = data[CommitType.Create];
|
||||||
if (createdSecrets && createdSecrets?.length) {
|
if (createdSecrets && createdSecrets?.length) {
|
||||||
const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({
|
const { keyName2BlindIndex } = await secretService.fnSecretBlindIndexCheck({
|
||||||
inputSecrets: createdSecrets,
|
inputSecrets: createdSecrets,
|
||||||
folderId,
|
folderId,
|
||||||
isNew: true,
|
isNew: true,
|
||||||
@@ -550,7 +517,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
commits.push(
|
commits.push(
|
||||||
...createdSecrets.map(({ secretName, ...el }) => ({
|
...createdSecrets.map(({ secretName, ...el }) => ({
|
||||||
...el,
|
...el,
|
||||||
op: SecretOperations.Create as const,
|
op: CommitType.Create as const,
|
||||||
version: 1,
|
version: 1,
|
||||||
secretBlindIndex: keyName2BlindIndex[secretName],
|
secretBlindIndex: keyName2BlindIndex[secretName],
|
||||||
algorithm: SecretEncryptionAlgo.AES_256_GCM,
|
algorithm: SecretEncryptionAlgo.AES_256_GCM,
|
||||||
@@ -562,12 +529,12 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
// not secret approval for update operations
|
// not secret approval for update operations
|
||||||
const updatedSecrets = data[SecretOperations.Update];
|
const updatedSecrets = data[CommitType.Update];
|
||||||
if (updatedSecrets && updatedSecrets?.length) {
|
if (updatedSecrets && updatedSecrets?.length) {
|
||||||
// get all blind index
|
// get all blind index
|
||||||
// Find all those secrets
|
// Find all those secrets
|
||||||
// if not throw not found
|
// if not throw not found
|
||||||
const { keyName2BlindIndex, secrets: secretsToBeUpdated } = await fnSecretBlindIndexCheck({
|
const { keyName2BlindIndex, secrets: secretsToBeUpdated } = await secretService.fnSecretBlindIndexCheck({
|
||||||
inputSecrets: updatedSecrets,
|
inputSecrets: updatedSecrets,
|
||||||
folderId,
|
folderId,
|
||||||
isNew: false,
|
isNew: false,
|
||||||
@@ -578,8 +545,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
// now find any secret that needs to update its name
|
// now find any secret that needs to update its name
|
||||||
// same process as above
|
// same process as above
|
||||||
const nameUpdatedSecrets = updatedSecrets.filter(({ newSecretName }) => Boolean(newSecretName));
|
const nameUpdatedSecrets = updatedSecrets.filter(({ newSecretName }) => Boolean(newSecretName));
|
||||||
const { keyName2BlindIndex: newKeyName2BlindIndex } = await fnSecretBlindIndexCheck({
|
const { keyName2BlindIndex: newKeyName2BlindIndex } = await secretService.fnSecretBlindIndexCheck({
|
||||||
inputSecrets: nameUpdatedSecrets.map(({ newSecretName }) => ({ secretName: newSecretName as string })),
|
inputSecrets: nameUpdatedSecrets,
|
||||||
folderId,
|
folderId,
|
||||||
isNew: true,
|
isNew: true,
|
||||||
blindIndexCfg,
|
blindIndexCfg,
|
||||||
@@ -596,14 +563,14 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
const secretId = secsGroupedByBlindIndex[keyName2BlindIndex[secretName]][0].id;
|
const secretId = secsGroupedByBlindIndex[keyName2BlindIndex[secretName]][0].id;
|
||||||
const secretBlindIndex =
|
const secretBlindIndex =
|
||||||
newSecretName && newKeyName2BlindIndex[newSecretName]
|
newSecretName && newKeyName2BlindIndex[newSecretName]
|
||||||
? newKeyName2BlindIndex?.[newSecretName]
|
? newKeyName2BlindIndex?.[secretName]
|
||||||
: keyName2BlindIndex[secretName];
|
: keyName2BlindIndex[secretName];
|
||||||
// add tags
|
// add tags
|
||||||
if (tagIds?.length) commitTagIds[keyName2BlindIndex[secretName]] = tagIds;
|
if (tagIds?.length) commitTagIds[keyName2BlindIndex[secretName]] = tagIds;
|
||||||
return {
|
return {
|
||||||
...latestSecretVersions[secretId],
|
...latestSecretVersions[secretId],
|
||||||
...el,
|
...el,
|
||||||
op: SecretOperations.Update as const,
|
op: CommitType.Update as const,
|
||||||
secret: secretId,
|
secret: secretId,
|
||||||
secretVersion: latestSecretVersions[secretId].id,
|
secretVersion: latestSecretVersions[secretId].id,
|
||||||
secretBlindIndex,
|
secretBlindIndex,
|
||||||
@@ -613,12 +580,12 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// deleted secrets
|
// deleted secrets
|
||||||
const deletedSecrets = data[SecretOperations.Delete];
|
const deletedSecrets = data[CommitType.Delete];
|
||||||
if (deletedSecrets && deletedSecrets.length) {
|
if (deletedSecrets && deletedSecrets.length) {
|
||||||
// get all blind index
|
// get all blind index
|
||||||
// Find all those secrets
|
// Find all those secrets
|
||||||
// if not throw not found
|
// if not throw not found
|
||||||
const { keyName2BlindIndex, secrets } = await fnSecretBlindIndexCheck({
|
const { keyName2BlindIndex, secrets } = await secretService.fnSecretBlindIndexCheck({
|
||||||
inputSecrets: deletedSecrets,
|
inputSecrets: deletedSecrets,
|
||||||
folderId,
|
folderId,
|
||||||
isNew: false,
|
isNew: false,
|
||||||
@@ -639,7 +606,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
if (!latestSecretVersions[secretId].secretBlindIndex)
|
if (!latestSecretVersions[secretId].secretBlindIndex)
|
||||||
throw new BadRequestError({ message: "Failed to find secret blind index" });
|
throw new BadRequestError({ message: "Failed to find secret blind index" });
|
||||||
return {
|
return {
|
||||||
op: SecretOperations.Delete as const,
|
op: CommitType.Delete as const,
|
||||||
...latestSecretVersions[secretId],
|
...latestSecretVersions[secretId],
|
||||||
secretBlindIndex: latestSecretVersions[secretId].secretBlindIndex as string,
|
secretBlindIndex: latestSecretVersions[secretId].secretBlindIndex as string,
|
||||||
secret: secretId,
|
secret: secretId,
|
||||||
@@ -663,7 +630,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
policyId: policy.id,
|
policyId: policy.id,
|
||||||
status: "open",
|
status: "open",
|
||||||
hasMerged: false,
|
hasMerged: false,
|
||||||
committerId: membership.id
|
committerUserId: actorId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user