mirror of
https://github.com/Infisical/infisical.git
synced 2025-07-29 22:37:44 +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
|
||||
- name: Download task definition
|
||||
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
|
||||
id: render-web-container
|
||||
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
||||
with:
|
||||
task-definition: task-definition.json
|
||||
container-name: infisical-core-platform
|
||||
container-name: infisical-prod-platform
|
||||
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||
environment-variables: "LOG_LEVEL=info"
|
||||
- name: Deploy to Amazon ECS service
|
||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
||||
with:
|
||||
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
||||
service: infisical-core-platform
|
||||
cluster: infisical-core-platform
|
||||
service: infisical-prod-platform
|
||||
cluster: infisical-prod-platform
|
||||
wait-for-service-stability: true
|
||||
|
||||
production-postgres-deployment:
|
||||
@@ -122,19 +122,19 @@ jobs:
|
||||
uses: pr-mpt/actions-commit-hash@v2
|
||||
- name: Download task definition
|
||||
run: |
|
||||
aws ecs describe-task-definition --task-definition infisical-core-platform --query taskDefinition > task-definition.json
|
||||
aws ecs describe-task-definition --task-definition infisical-prod-platform --query taskDefinition > task-definition.json
|
||||
- name: Render Amazon ECS task definition
|
||||
id: render-web-container
|
||||
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
||||
with:
|
||||
task-definition: task-definition.json
|
||||
container-name: infisical-core-platform
|
||||
container-name: infisical-prod-platform
|
||||
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||
environment-variables: "LOG_LEVEL=info"
|
||||
- name: Deploy to Amazon ECS service
|
||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
||||
with:
|
||||
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
||||
service: infisical-core-platform
|
||||
cluster: infisical-core-platform
|
||||
service: infisical-prod-platform
|
||||
cluster: infisical-prod-platform
|
||||
wait-for-service-stability: true
|
||||
|
@@ -40,14 +40,13 @@ jobs:
|
||||
REDIS_URL: redis://172.17.0.1:6379
|
||||
DB_CONNECTION_URI: postgres://infisical:infisical@172.17.0.1:5432/infisical?sslmode=disable
|
||||
JWT_AUTH_SECRET: something-random
|
||||
ENCRYPTION_KEY: 4bnfe4e407b8921c104518903515b218
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21.5'
|
||||
- name: Wait for container to be stable and check logs
|
||||
run: |
|
||||
SECONDS=0
|
||||
r HEALTHY=0
|
||||
HEALTHY=0
|
||||
while [ $SECONDS -lt 60 ]; do
|
||||
if docker ps | grep infisical-api | grep -q healthy; then
|
||||
echo "Container is healthy."
|
||||
@@ -74,4 +73,4 @@ jobs:
|
||||
run: |
|
||||
docker-compose -f "docker-compose.dev.yml" down
|
||||
docker stop infisical-api
|
||||
docker remove infisical-api
|
||||
docker remove infisical-api
|
@@ -38,16 +38,6 @@ jobs:
|
||||
rm added_files.txt
|
||||
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
|
||||
if: env.SKIP_RENAME != 'true'
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
@@ -56,4 +46,3 @@ jobs:
|
||||
commit-message: 'chore: renamed new migration files to latest UTC (gh-action)'
|
||||
title: 'GH Action: rename new migration file 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/MemberListTab/MemberRoleForm/MemberRbacSection.tsx:generic-api-key:206
|
||||
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
|
||||
|
@@ -1,6 +1,7 @@
|
||||
ARG POSTHOG_HOST=https://app.posthog.com
|
||||
ARG POSTHOG_API_KEY=posthog-api-key
|
||||
ARG INTERCOM_ID=intercom-id
|
||||
ARG SAML_ORG_SLUG=saml-org-slug-default
|
||||
|
||||
FROM node:20-alpine AS base
|
||||
|
||||
@@ -34,7 +35,9 @@ ENV NEXT_PUBLIC_POSTHOG_API_KEY $POSTHOG_API_KEY
|
||||
ARG INTERCOM_ID
|
||||
ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID
|
||||
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
|
||||
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 --from=frontend-builder /app/public ./public
|
||||
RUN chown non-root-user:nodejs ./public/data
|
||||
|
||||
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/standalone ./
|
||||
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/static ./.next/static
|
||||
|
||||
@@ -91,18 +93,9 @@ RUN mkdir frontend-build
|
||||
|
||||
# Production stage
|
||||
FROM base AS production
|
||||
RUN apk add --upgrade --no-cache ca-certificates
|
||||
RUN addgroup --system --gid 1001 nodejs \
|
||||
&& 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
|
||||
ARG 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
|
||||
ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
|
||||
BAKED_NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID
|
||||
ARG SAML_ORG_SLUG
|
||||
ENV NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG \
|
||||
BAKED_NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG
|
||||
|
||||
WORKDIR /
|
||||
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { Lock } from "@app/lib/red-lock";
|
||||
|
||||
export const mockKeyStore = (): TKeyStoreFactory => {
|
||||
const store: Record<string, string | number | Buffer> = {};
|
||||
@@ -26,12 +25,6 @@ export const mockKeyStore = (): TKeyStoreFactory => {
|
||||
},
|
||||
incrementBy: async () => {
|
||||
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-retry": "^4.0.0",
|
||||
"bcrypt": "^5.1.1",
|
||||
"bullmq": "^5.4.2",
|
||||
"bullmq": "^5.3.3",
|
||||
"cassandra-driver": "^4.7.2",
|
||||
"dotenv": "^16.4.1",
|
||||
"fastify": "^4.26.0",
|
||||
"fastify-plugin": "^4.5.1",
|
||||
"google-auth-library": "^9.9.0",
|
||||
"googleapis": "^137.1.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"ioredis": "^5.3.2",
|
||||
"jmespath": "^0.16.0",
|
||||
@@ -112,7 +110,7 @@
|
||||
"libsodium-wrappers": "^0.7.13",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"ms": "^2.1.3",
|
||||
"mysql2": "^3.9.8",
|
||||
"mysql2": "^3.9.4",
|
||||
"nanoid": "^5.0.4",
|
||||
"nodemailer": "^6.9.9",
|
||||
"ora": "^7.0.1",
|
||||
|
@@ -35,8 +35,6 @@ const getZodPrimitiveType = (type: string) => {
|
||||
return "z.coerce.number()";
|
||||
case "text":
|
||||
return "z.string()";
|
||||
case "bytea":
|
||||
return "zodBuffer";
|
||||
default:
|
||||
throw new Error(`Invalid type: ${type}`);
|
||||
}
|
||||
@@ -98,15 +96,10 @@ const main = async () => {
|
||||
const columnNames = Object.keys(columns);
|
||||
|
||||
let schema = "";
|
||||
const zodImportSet = new Set<string>();
|
||||
for (let colNum = 0; colNum < columnNames.length; colNum++) {
|
||||
const columnName = columnNames[colNum];
|
||||
const colInfo = columns[columnName];
|
||||
let ztype = getZodPrimitiveType(colInfo.type);
|
||||
if (["zodBuffer"].includes(ztype)) {
|
||||
zodImportSet.add(ztype);
|
||||
}
|
||||
|
||||
// don't put optional on id
|
||||
if (colInfo.defaultValue && columnName !== "id") {
|
||||
const { defaultValue } = colInfo;
|
||||
@@ -128,8 +121,6 @@ const main = async () => {
|
||||
.split("_")
|
||||
.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
|
||||
writeFileSync(
|
||||
path.join(__dirname, "../src/db/schemas", `${dashcase}.ts`),
|
||||
@@ -140,8 +131,6 @@ const main = async () => {
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
${zodImports.length ? `import { ${zodImports.join(",")} } from \"@app/lib/zod\";` : ""}
|
||||
|
||||
import { TImmutableDBKeys } from "./models";
|
||||
|
||||
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 { TIdentityServiceFactory } from "@app/services/identity/identity-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 { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-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 { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-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 { TServiceTokenServiceFactory } from "@app/services/service-token/service-token-service";
|
||||
import { TSuperAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
|
||||
@@ -109,7 +103,6 @@ declare module "fastify" {
|
||||
projectKey: TProjectKeyServiceFactory;
|
||||
projectRole: TProjectRoleServiceFactory;
|
||||
secret: TSecretServiceFactory;
|
||||
secretReplication: TSecretReplicationServiceFactory;
|
||||
secretTag: TSecretTagServiceFactory;
|
||||
secretImport: TSecretImportServiceFactory;
|
||||
projectBot: TProjectBotServiceFactory;
|
||||
@@ -122,10 +115,6 @@ declare module "fastify" {
|
||||
identityAccessToken: TIdentityAccessTokenServiceFactory;
|
||||
identityProject: TIdentityProjectServiceFactory;
|
||||
identityUa: TIdentityUaServiceFactory;
|
||||
identityKubernetesAuth: TIdentityKubernetesAuthServiceFactory;
|
||||
identityGcpAuth: TIdentityGcpAuthServiceFactory;
|
||||
identityAwsAuth: TIdentityAwsAuthServiceFactory;
|
||||
identityAzureAuth: TIdentityAzureAuthServiceFactory;
|
||||
accessApprovalPolicy: TAccessApprovalPolicyServiceFactory;
|
||||
accessApprovalRequest: TAccessApprovalRequestServiceFactory;
|
||||
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
|
||||
@@ -146,7 +135,6 @@ declare module "fastify" {
|
||||
dynamicSecretLease: TDynamicSecretLeaseServiceFactory;
|
||||
projectUserAdditionalPrivilege: TProjectUserAdditionalPrivilegeServiceFactory;
|
||||
identityProjectAdditionalPrivilege: TIdentityProjectAdditionalPrivilegeServiceFactory;
|
||||
secretSharing: TSecretSharingServiceFactory;
|
||||
};
|
||||
// this is exclusive use for middlewares in which we need to inject data
|
||||
// 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,
|
||||
TGroupProjectMembershipsInsert,
|
||||
TGroupProjectMembershipsUpdate,
|
||||
TGroupProjectUserAdditionalPrivilege,
|
||||
TGroupProjectUserAdditionalPrivilegeInsert,
|
||||
TGroupProjectUserAdditionalPrivilegeUpdate,
|
||||
TGroups,
|
||||
TGroupsInsert,
|
||||
TGroupsUpdate,
|
||||
@@ -59,18 +62,6 @@ import {
|
||||
TIdentityAccessTokens,
|
||||
TIdentityAccessTokensInsert,
|
||||
TIdentityAccessTokensUpdate,
|
||||
TIdentityAwsAuths,
|
||||
TIdentityAwsAuthsInsert,
|
||||
TIdentityAwsAuthsUpdate,
|
||||
TIdentityAzureAuths,
|
||||
TIdentityAzureAuthsInsert,
|
||||
TIdentityAzureAuthsUpdate,
|
||||
TIdentityGcpAuths,
|
||||
TIdentityGcpAuthsInsert,
|
||||
TIdentityGcpAuthsUpdate,
|
||||
TIdentityKubernetesAuths,
|
||||
TIdentityKubernetesAuthsInsert,
|
||||
TIdentityKubernetesAuthsUpdate,
|
||||
TIdentityOrgMemberships,
|
||||
TIdentityOrgMembershipsInsert,
|
||||
TIdentityOrgMembershipsUpdate,
|
||||
@@ -98,15 +89,6 @@ import {
|
||||
TIntegrations,
|
||||
TIntegrationsInsert,
|
||||
TIntegrationsUpdate,
|
||||
TKmsKeys,
|
||||
TKmsKeysInsert,
|
||||
TKmsKeysUpdate,
|
||||
TKmsKeyVersions,
|
||||
TKmsKeyVersionsInsert,
|
||||
TKmsKeyVersionsUpdate,
|
||||
TKmsRootConfig,
|
||||
TKmsRootConfigInsert,
|
||||
TKmsRootConfigUpdate,
|
||||
TLdapConfigs,
|
||||
TLdapConfigsInsert,
|
||||
TLdapConfigsUpdate,
|
||||
@@ -185,9 +167,6 @@ import {
|
||||
TSecretImports,
|
||||
TSecretImportsInsert,
|
||||
TSecretImportsUpdate,
|
||||
TSecretReferences,
|
||||
TSecretReferencesInsert,
|
||||
TSecretReferencesUpdate,
|
||||
TSecretRotationOutputs,
|
||||
TSecretRotationOutputsInsert,
|
||||
TSecretRotationOutputsUpdate,
|
||||
@@ -198,9 +177,6 @@ import {
|
||||
TSecretScanningGitRisks,
|
||||
TSecretScanningGitRisksInsert,
|
||||
TSecretScanningGitRisksUpdate,
|
||||
TSecretSharing,
|
||||
TSecretSharingInsert,
|
||||
TSecretSharingUpdate,
|
||||
TSecretsInsert,
|
||||
TSecretSnapshotFolders,
|
||||
TSecretSnapshotFoldersInsert,
|
||||
@@ -317,6 +293,11 @@ declare module "knex/types/tables" {
|
||||
TProjectUserMembershipRolesInsert,
|
||||
TProjectUserMembershipRolesUpdate
|
||||
>;
|
||||
[TableName.GroupProjectUserAdditionalPrivilege]: Knex.CompositeTableType<
|
||||
TGroupProjectUserAdditionalPrivilege,
|
||||
TGroupProjectUserAdditionalPrivilegeInsert,
|
||||
TGroupProjectUserAdditionalPrivilegeUpdate
|
||||
>;
|
||||
[TableName.ProjectRoles]: Knex.CompositeTableType<TProjectRoles, TProjectRolesInsert, TProjectRolesUpdate>;
|
||||
[TableName.ProjectUserAdditionalPrivilege]: Knex.CompositeTableType<
|
||||
TProjectUserAdditionalPrivilege,
|
||||
@@ -325,11 +306,6 @@ declare module "knex/types/tables" {
|
||||
>;
|
||||
[TableName.ProjectKeys]: Knex.CompositeTableType<TProjectKeys, TProjectKeysInsert, TProjectKeysUpdate>;
|
||||
[TableName.Secret]: Knex.CompositeTableType<TSecrets, TSecretsInsert, TSecretsUpdate>;
|
||||
[TableName.SecretReference]: Knex.CompositeTableType<
|
||||
TSecretReferences,
|
||||
TSecretReferencesInsert,
|
||||
TSecretReferencesUpdate
|
||||
>;
|
||||
[TableName.SecretBlindIndex]: Knex.CompositeTableType<
|
||||
TSecretBlindIndexes,
|
||||
TSecretBlindIndexesInsert,
|
||||
@@ -342,7 +318,6 @@ declare module "knex/types/tables" {
|
||||
TSecretFolderVersionsInsert,
|
||||
TSecretFolderVersionsUpdate
|
||||
>;
|
||||
[TableName.SecretSharing]: Knex.CompositeTableType<TSecretSharing, TSecretSharingInsert, TSecretSharingUpdate>;
|
||||
[TableName.SecretTag]: Knex.CompositeTableType<TSecretTags, TSecretTagsInsert, TSecretTagsUpdate>;
|
||||
[TableName.SecretImport]: Knex.CompositeTableType<TSecretImports, TSecretImportsInsert, TSecretImportsUpdate>;
|
||||
[TableName.Integration]: Knex.CompositeTableType<TIntegrations, TIntegrationsInsert, TIntegrationsUpdate>;
|
||||
@@ -359,26 +334,6 @@ declare module "knex/types/tables" {
|
||||
TIdentityUniversalAuthsInsert,
|
||||
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<
|
||||
TIdentityUaClientSecrets,
|
||||
TIdentityUaClientSecretsInsert,
|
||||
@@ -525,13 +480,5 @@ declare module "knex/types/tables" {
|
||||
TSecretVersionTagJunctionInsert,
|
||||
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.string("name").notNullable();
|
||||
t.integer("approvals").defaultTo(1).notNullable();
|
||||
t.string("secretPath");
|
||||
|
||||
t.uuid("envId").notNullable();
|
||||
t.string("secretPath");
|
||||
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
|
||||
t.timestamps(true, true, true);
|
||||
});
|
||||
@@ -21,8 +20,9 @@ export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasTable(TableName.AccessApprovalPolicyApprover))) {
|
||||
await knex.schema.createTable(TableName.AccessApprovalPolicyApprover, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.uuid("approverId").notNullable();
|
||||
t.foreign("approverId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
||||
|
||||
t.uuid("approverUserId").nullable();
|
||||
t.foreign("approverUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||
|
||||
t.uuid("policyId").notNullable();
|
||||
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.foreign("policyId").references("id").inTable(TableName.AccessApprovalPolicy).onDelete("CASCADE");
|
||||
|
||||
t.uuid("privilegeId").nullable();
|
||||
t.foreign("privilegeId").references("id").inTable(TableName.ProjectUserAdditionalPrivilege).onDelete("CASCADE");
|
||||
t.uuid("projectUserPrivilegeId").nullable();
|
||||
t.foreign("projectUserPrivilegeId")
|
||||
.references("id")
|
||||
.inTable(TableName.ProjectUserAdditionalPrivilege)
|
||||
.onDelete("CASCADE");
|
||||
|
||||
t.uuid("requestedBy").notNullable();
|
||||
t.foreign("requestedBy").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
||||
t.uuid("groupProjectUserPrivilegeId").nullable();
|
||||
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.
|
||||
t.boolean("isTemporary").notNullable();
|
||||
@@ -31,14 +46,17 @@ export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasTable(TableName.AccessApprovalRequestReviewer))) {
|
||||
await knex.schema.createTable(TableName.AccessApprovalRequestReviewer, (t) => {
|
||||
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.uuid("requestId").notNullable();
|
||||
t.foreign("requestId").references("id").inTable(TableName.AccessApprovalRequest).onDelete("CASCADE");
|
||||
t.timestamps(true, true, true);
|
||||
});
|
||||
}
|
||||
|
||||
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({
|
||||
id: z.string().uuid(),
|
||||
approverId: z.string().uuid(),
|
||||
approverUserId: z.string().uuid().nullable().optional(),
|
||||
policyId: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
|
@@ -11,8 +11,8 @@ export const AccessApprovalPoliciesSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
approvals: z.number().default(1),
|
||||
secretPath: z.string().nullable().optional(),
|
||||
envId: z.string().uuid(),
|
||||
secretPath: z.string().nullable().optional(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
});
|
||||
|
@@ -9,7 +9,7 @@ import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const AccessApprovalRequestsReviewersSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
member: z.string().uuid(),
|
||||
memberUserId: z.string().uuid(),
|
||||
status: z.string(),
|
||||
requestId: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
|
@@ -10,8 +10,11 @@ import { TImmutableDBKeys } from "./models";
|
||||
export const AccessApprovalRequestsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
policyId: z.string().uuid(),
|
||||
privilegeId: z.string().uuid().nullable().optional(),
|
||||
requestedBy: z.string().uuid(),
|
||||
projectUserPrivilegeId: z.string().uuid().nullable().optional(),
|
||||
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(),
|
||||
temporaryRange: z.string().nullable().optional(),
|
||||
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 "./group-project-membership-roles";
|
||||
export * from "./group-project-memberships";
|
||||
export * from "./group-project-user-additional-privilege";
|
||||
export * from "./groups";
|
||||
export * from "./identities";
|
||||
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-project-additional-privilege";
|
||||
export * from "./identity-project-membership-role";
|
||||
@@ -30,9 +27,6 @@ export * from "./identity-universal-auths";
|
||||
export * from "./incident-contacts";
|
||||
export * from "./integration-auths";
|
||||
export * from "./integrations";
|
||||
export * from "./kms-key-versions";
|
||||
export * from "./kms-keys";
|
||||
export * from "./kms-root-config";
|
||||
export * from "./ldap-configs";
|
||||
export * from "./ldap-group-maps";
|
||||
export * from "./models";
|
||||
@@ -60,11 +54,9 @@ export * from "./secret-blind-indexes";
|
||||
export * from "./secret-folder-versions";
|
||||
export * from "./secret-folders";
|
||||
export * from "./secret-imports";
|
||||
export * from "./secret-references";
|
||||
export * from "./secret-rotation-outputs";
|
||||
export * from "./secret-rotations";
|
||||
export * from "./secret-scanning-git-risks";
|
||||
export * from "./secret-sharing";
|
||||
export * from "./secret-snapshot-folders";
|
||||
export * from "./secret-snapshot-secrets";
|
||||
export * from "./secret-snapshots";
|
||||
|
@@ -28,10 +28,7 @@ export const IntegrationsSchema = z.object({
|
||||
secretPath: z.string().default("/"),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
lastUsed: z.date().nullable().optional(),
|
||||
isSynced: z.boolean().nullable().optional(),
|
||||
syncMessage: z.string().nullable().optional(),
|
||||
lastSyncJobId: z.string().nullable().optional()
|
||||
lastUsed: z.date().nullable().optional()
|
||||
});
|
||||
|
||||
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",
|
||||
ProjectRoles = "project_roles",
|
||||
ProjectUserAdditionalPrivilege = "project_user_additional_privilege",
|
||||
GroupProjectUserAdditionalPrivilege = "group_project_user_additional_privilege",
|
||||
ProjectUserMembershipRole = "project_user_membership_roles",
|
||||
ProjectKeys = "project_keys",
|
||||
Secret = "secrets",
|
||||
SecretReference = "secret_references",
|
||||
SecretSharing = "secret_sharing",
|
||||
SecretBlindIndex = "secret_blind_indexes",
|
||||
SecretVersion = "secret_versions",
|
||||
SecretFolder = "secret_folders",
|
||||
@@ -46,11 +45,7 @@ export enum TableName {
|
||||
Identity = "identities",
|
||||
IdentityAccessToken = "identity_access_tokens",
|
||||
IdentityUniversalAuth = "identity_universal_auths",
|
||||
IdentityKubernetesAuth = "identity_kubernetes_auths",
|
||||
IdentityGcpAuth = "identity_gcp_auths",
|
||||
IdentityAzureAuth = "identity_azure_auths",
|
||||
IdentityUaClientSecret = "identity_ua_client_secrets",
|
||||
IdentityAwsAuth = "identity_aws_auths",
|
||||
IdentityOrgMembership = "identity_org_memberships",
|
||||
IdentityProjectMembership = "identity_project_memberships",
|
||||
IdentityProjectMembershipRole = "identity_project_membership_role",
|
||||
@@ -81,11 +76,7 @@ export enum TableName {
|
||||
DynamicSecretLease = "dynamic_secret_leases",
|
||||
// junction tables with tags
|
||||
JnSecretTag = "secret_tag_junction",
|
||||
SecretVersionTag = "secret_version_tag_junction",
|
||||
// KMS Service
|
||||
KmsServerRootConfig = "kms_root_config",
|
||||
KmsKey = "kms_keys",
|
||||
KmsKeyVersion = "kms_key_versions"
|
||||
SecretVersionTag = "secret_version_tag_junction"
|
||||
}
|
||||
|
||||
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
|
||||
@@ -152,9 +143,5 @@ export enum ProjectUpgradeStatus {
|
||||
}
|
||||
|
||||
export enum IdentityAuthMethod {
|
||||
Univeral = "universal-auth",
|
||||
KUBERNETES_AUTH = "kubernetes-auth",
|
||||
GCP_AUTH = "gcp-auth",
|
||||
AWS_AUTH = "aws-auth",
|
||||
AZURE_AUTH = "azure-auth"
|
||||
Univeral = "universal-auth"
|
||||
}
|
||||
|
@@ -9,10 +9,11 @@ import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const SecretApprovalPoliciesApproversSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
approverId: z.string().uuid(),
|
||||
approverId: z.string().uuid().nullable().optional(),
|
||||
policyId: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
updatedAt: z.date(),
|
||||
approverUserId: z.string().uuid().nullable().optional()
|
||||
});
|
||||
|
||||
export type TSecretApprovalPoliciesApprovers = z.infer<typeof SecretApprovalPoliciesApproversSchema>;
|
||||
|
@@ -9,11 +9,12 @@ import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const SecretApprovalRequestsReviewersSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
member: z.string().uuid(),
|
||||
member: z.string().uuid().nullable().optional(),
|
||||
status: z.string(),
|
||||
requestId: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
updatedAt: z.date(),
|
||||
memberUserId: z.string().uuid().nullable().optional()
|
||||
});
|
||||
|
||||
export type TSecretApprovalRequestsReviewers = z.infer<typeof SecretApprovalRequestsReviewersSchema>;
|
||||
|
@@ -16,10 +16,11 @@ export const SecretApprovalRequestsSchema = z.object({
|
||||
slug: z.string(),
|
||||
folderId: z.string().uuid(),
|
||||
statusChangeBy: z.string().uuid().nullable().optional(),
|
||||
committerId: z.string().uuid(),
|
||||
committerId: z.string().uuid().nullable().optional(),
|
||||
createdAt: 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>;
|
||||
|
@@ -14,8 +14,7 @@ export const SecretFoldersSchema = z.object({
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
envId: z.string().uuid(),
|
||||
parentId: z.string().uuid().nullable().optional(),
|
||||
isReserved: z.boolean().default(false).nullable().optional()
|
||||
parentId: z.string().uuid().nullable().optional()
|
||||
});
|
||||
|
||||
export type TSecretFolders = z.infer<typeof SecretFoldersSchema>;
|
||||
|
@@ -15,12 +15,7 @@ export const SecretImportsSchema = z.object({
|
||||
position: z.number(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
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()
|
||||
folderId: z.string().uuid()
|
||||
});
|
||||
|
||||
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(),
|
||||
updatedAt: z.date(),
|
||||
allowedSignUpDomain: z.string().nullable().optional(),
|
||||
instanceId: z.string().uuid().default("00000000-0000-0000-0000-000000000000"),
|
||||
trustSamlEmails: z.boolean().default(false).nullable().optional(),
|
||||
trustLdapEmails: z.boolean().default(false).nullable().optional()
|
||||
instanceId: z.string().uuid().default("00000000-0000-0000-0000-000000000000")
|
||||
});
|
||||
|
||||
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
||||
|
@@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models";
|
||||
export const UserAliasesSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
userId: z.string().uuid(),
|
||||
username: z.string().nullable().optional(),
|
||||
username: z.string(),
|
||||
aliasType: z.string(),
|
||||
externalId: z.string(),
|
||||
emails: z.string().array().nullable().optional(),
|
||||
|
@@ -21,11 +21,7 @@ export const UsersSchema = z.object({
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
isGhost: z.boolean().default(false),
|
||||
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()
|
||||
username: z.string()
|
||||
});
|
||||
|
||||
export type TUsers = z.infer<typeof UsersSchema>;
|
||||
|
@@ -53,7 +53,9 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
}),
|
||||
response: {
|
||||
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: {
|
||||
querystring: z.object({
|
||||
projectSlug: z.string().trim(),
|
||||
authorProjectMembershipId: z.string().trim().optional(),
|
||||
authorUserId: z.string().trim().optional(),
|
||||
envSlug: z.string().trim().optional()
|
||||
}),
|
||||
response: {
|
||||
@@ -84,7 +84,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
||||
isApproved: z.boolean(),
|
||||
privilege: z
|
||||
.object({
|
||||
membershipId: z.string(),
|
||||
projectMembershipId: z.string().nullish(),
|
||||
groupMembershipId: z.string().nullish(),
|
||||
isTemporary: z.boolean(),
|
||||
temporaryMode: z.string().nullish(),
|
||||
temporaryRange: z.string().nullish(),
|
||||
@@ -115,8 +116,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
||||
handler: async (req) => {
|
||||
const { requests } = await server.services.accessApprovalRequest.listApprovalRequests({
|
||||
projectSlug: req.query.projectSlug,
|
||||
authorProjectMembershipId: req.query.authorProjectMembershipId,
|
||||
envSlug: req.query.envSlug,
|
||||
authorUserId: req.query.authorUserId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
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({
|
||||
url: "/:requestId/review",
|
||||
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 ms from "ms";
|
||||
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 { ProjectPermissionSet } from "@app/ee/services/permission/project-permission";
|
||||
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
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";
|
||||
|
||||
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
||||
@@ -44,33 +41,16 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
})
|
||||
.optional()
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||
permissions: ProjectPermissionSchema.array()
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
||||
.optional(),
|
||||
privilegePermission: ProjectSpecificPrivilegePermissionSchema.describe(
|
||||
IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.privilegePermission
|
||||
).optional()
|
||||
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: SanitizedIdentityPrivilegeSchema
|
||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
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({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
@@ -79,7 +59,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
...req.body,
|
||||
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
||||
isTemporary: false,
|
||||
permissions: JSON.stringify(packRules(permission))
|
||||
permissions: JSON.stringify(packRules(req.body.permissions))
|
||||
});
|
||||
return { privilege };
|
||||
}
|
||||
@@ -112,12 +92,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
})
|
||||
.optional()
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||
permissions: ProjectPermissionSchema.array()
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
||||
.optional(),
|
||||
privilegePermission: ProjectSpecificPrivilegePermissionSchema.describe(
|
||||
IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.privilegePermission
|
||||
).optional(),
|
||||
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions),
|
||||
temporaryMode: z
|
||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
||||
@@ -132,25 +107,12 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: SanitizedIdentityPrivilegeSchema
|
||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
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({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
@@ -159,7 +121,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
...req.body,
|
||||
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
||||
isTemporary: true,
|
||||
permissions: JSON.stringify(packRules(permission))
|
||||
permissions: JSON.stringify(packRules(req.body.permissions))
|
||||
});
|
||||
return { privilege };
|
||||
}
|
||||
@@ -195,17 +157,14 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
message: "Slug must be a valid slug"
|
||||
})
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.newSlug),
|
||||
permissions: ProjectPermissionSchema.array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.permissions),
|
||||
privilegePermission: ProjectSpecificPrivilegePermissionSchema.describe(
|
||||
IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.privilegePermission
|
||||
).optional(),
|
||||
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.permissions),
|
||||
isTemporary: z.boolean().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
|
||||
temporaryMode: z
|
||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
|
||||
temporaryRange: z
|
||||
.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),
|
||||
temporaryAccessStartTime: z
|
||||
.string()
|
||||
@@ -216,24 +175,13 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: SanitizedIdentityPrivilegeSchema
|
||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { permissions, privilegePermission, ...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 updatedInfo = req.body.privilegeDetails;
|
||||
const privilege = await server.services.identityProjectAdditionalPrivilege.updateBySlug({
|
||||
actorId: req.permission.id,
|
||||
actor: req.permission.type,
|
||||
@@ -244,7 +192,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
projectSlug: req.body.projectSlug,
|
||||
data: {
|
||||
...updatedInfo,
|
||||
permissions: permission ? JSON.stringify(packRules(permission)) : undefined
|
||||
permissions: updatedInfo?.permissions ? JSON.stringify(packRules(updatedInfo.permissions)) : undefined
|
||||
}
|
||||
});
|
||||
return { privilege };
|
||||
@@ -271,7 +219,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: SanitizedIdentityPrivilegeSchema
|
||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -312,7 +260,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
privilege: SanitizedIdentityPrivilegeSchema
|
||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -345,11 +293,16 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
],
|
||||
querystring: z.object({
|
||||
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: {
|
||||
200: z.object({
|
||||
privileges: SanitizedIdentityPrivilegeSchema.array()
|
||||
privileges: IdentityProjectAdditionalPrivilegeSchema.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -362,9 +315,15 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.query
|
||||
});
|
||||
return {
|
||||
privileges
|
||||
};
|
||||
if (req.query.unpacked) {
|
||||
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 { registerAccessApprovalRequestRouter } from "./access-approval-request-router";
|
||||
import { registerAuditLogStreamRouter } from "./audit-log-stream-router";
|
||||
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
|
||||
import { registerDynamicSecretRouter } from "./dynamic-secret-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 { isValidLdapFilter, searchGroups } from "@app/ee/services/ldap-config/ldap-fns";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
@@ -53,7 +52,6 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
||||
// eslint-disable-next-line
|
||||
async (req: IncomingMessage, user, cb) => {
|
||||
try {
|
||||
if (!user.email) throw new BadRequestError({ message: "Invalid request. Missing email." });
|
||||
const ldapConfig = (req as unknown as FastifyRequest).ldapConfig as TLDAPConfig;
|
||||
|
||||
let groups: { dn: string; cn: string }[] | undefined;
|
||||
@@ -76,7 +74,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
||||
username: user.uid,
|
||||
firstName: user.givenName ?? user.cn ?? "",
|
||||
lastName: user.sn ?? "",
|
||||
email: user.mail,
|
||||
emails: user.mail ? [user.mail] : [],
|
||||
groups,
|
||||
relayState: ((req as unknown as FastifyRequest).body as { RelayState?: string }).RelayState,
|
||||
orgId: (req as unknown as FastifyRequest).ldapConfig.organization
|
||||
|
@@ -23,7 +23,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
.min(1)
|
||||
.trim()
|
||||
.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"
|
||||
)
|
||||
.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 { ProjectMembershipRole, ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
|
||||
import { PROJECT_ROLE } from "@app/lib/api-docs";
|
||||
import { ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
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";
|
||||
|
||||
export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:projectSlug/roles",
|
||||
url: "/:projectId/roles",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Create a project role",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
projectSlug: z.string().trim().describe(PROJECT_ROLE.CREATE.projectSlug)
|
||||
projectId: z.string().trim()
|
||||
}),
|
||||
body: z.object({
|
||||
slug: z
|
||||
.string()
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.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)
|
||||
slug: z.string().trim(),
|
||||
name: z.string().trim(),
|
||||
description: z.string().trim().optional(),
|
||||
permissions: z.any().array()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
role: SanitizedRoleSchema
|
||||
role: ProjectRolesSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const role = await server.services.projectRole.createRole({
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actor: req.permission.type,
|
||||
projectSlug: req.params.projectSlug,
|
||||
data: {
|
||||
...req.body,
|
||||
permissions: JSON.stringify(packRules(req.body.permissions))
|
||||
}
|
||||
});
|
||||
const role = await server.services.projectRole.createRole(
|
||||
req.permission.type,
|
||||
req.permission.id,
|
||||
req.params.projectId,
|
||||
req.body,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { role };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:projectSlug/roles/:roleId",
|
||||
url: "/:projectId/roles/:roleId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Update a project role",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
projectSlug: z.string().trim().describe(PROJECT_ROLE.UPDATE.projectSlug),
|
||||
roleId: z.string().trim().describe(PROJECT_ROLE.UPDATE.roleId)
|
||||
projectId: z.string().trim(),
|
||||
roleId: z.string().trim()
|
||||
}),
|
||||
body: z.object({
|
||||
slug: z
|
||||
.string()
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.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)
|
||||
slug: z.string().trim().optional(),
|
||||
name: z.string().trim().optional(),
|
||||
description: z.string().trim().optional(),
|
||||
permissions: z.any().array()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
role: SanitizedRoleSchema
|
||||
role: ProjectRolesSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const role = await server.services.projectRole.updateRole({
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actor: req.permission.type,
|
||||
projectSlug: req.params.projectSlug,
|
||||
roleId: req.params.roleId,
|
||||
data: {
|
||||
...req.body,
|
||||
permissions: JSON.stringify(packRules(req.body.permissions))
|
||||
}
|
||||
});
|
||||
const role = await server.services.projectRole.updateRole(
|
||||
req.permission.type,
|
||||
req.permission.id,
|
||||
req.params.projectId,
|
||||
req.params.roleId,
|
||||
req.body,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { role };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:projectSlug/roles/:roleId",
|
||||
url: "/:projectId/roles/:roleId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Delete a project role",
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
],
|
||||
params: z.object({
|
||||
projectSlug: z.string().trim().describe(PROJECT_ROLE.DELETE.projectSlug),
|
||||
roleId: z.string().trim().describe(PROJECT_ROLE.DELETE.roleId)
|
||||
projectId: z.string().trim(),
|
||||
roleId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
role: SanitizedRoleSchema
|
||||
role: ProjectRolesSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const role = await server.services.projectRole.deleteRole({
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actor: req.permission.type,
|
||||
projectSlug: req.params.projectSlug,
|
||||
roleId: req.params.roleId
|
||||
});
|
||||
const role = await server.services.projectRole.deleteRole(
|
||||
req.permission.type,
|
||||
req.permission.id,
|
||||
req.params.projectId,
|
||||
req.params.roleId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { role };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:projectSlug/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",
|
||||
url: "/:projectId/roles",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectSlug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.projectSlug),
|
||||
slug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.roleSlug)
|
||||
projectId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
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) => {
|
||||
const role = await server.services.projectRole.getRoleBySlug({
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorId: req.permission.id,
|
||||
actorOrgId: req.permission.orgId,
|
||||
actor: req.permission.type,
|
||||
projectSlug: req.params.projectSlug,
|
||||
roleSlug: req.params.slug
|
||||
});
|
||||
return { role };
|
||||
const roles = await server.services.projectRole.listRoles(
|
||||
req.permission.type,
|
||||
req.permission.id,
|
||||
req.params.projectId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
return { data: { roles } };
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -102,12 +102,12 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
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
|
||||
|
||||
if (!email || !profile.firstName) {
|
||||
if (!profile.email || !profile.firstName) {
|
||||
throw new BadRequestError({ message: "Invalid request. Missing email or first name" });
|
||||
}
|
||||
|
||||
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
|
||||
externalId: profile.nameID,
|
||||
username: profile.nameID ?? email,
|
||||
email,
|
||||
firstName: profile.firstName as string,
|
||||
lastName: profile.lastName as string,
|
||||
|
@@ -153,7 +153,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const users = await req.server.services.scim.listScimUsers({
|
||||
startIndex: req.query.startIndex,
|
||||
offset: req.query.startIndex,
|
||||
limit: req.query.count,
|
||||
filter: req.query.filter,
|
||||
orgId: req.permission.orgId
|
||||
@@ -163,11 +163,11 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/Users/:orgMembershipId",
|
||||
url: "/Users/:userId",
|
||||
method: "GET",
|
||||
schema: {
|
||||
params: z.object({
|
||||
orgMembershipId: z.string().trim()
|
||||
userId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
201: z.object({
|
||||
@@ -193,7 +193,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const user = await req.server.services.scim.getScimUser({
|
||||
orgMembershipId: req.params.orgMembershipId,
|
||||
userId: req.params.userId,
|
||||
orgId: req.permission.orgId
|
||||
});
|
||||
return user;
|
||||
@@ -249,7 +249,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
const primaryEmail = req.body.emails?.find((email) => email.primary)?.value;
|
||||
|
||||
const user = await req.server.services.scim.createScimUser({
|
||||
externalId: req.body.userName,
|
||||
username: req.body.userName,
|
||||
email: primaryEmail,
|
||||
firstName: req.body.name.givenName,
|
||||
lastName: req.body.name.familyName,
|
||||
@@ -261,11 +261,11 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/Users/:orgMembershipId",
|
||||
url: "/Users/:userId",
|
||||
method: "DELETE",
|
||||
schema: {
|
||||
params: z.object({
|
||||
orgMembershipId: z.string().trim()
|
||||
userId: z.string().trim()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({})
|
||||
@@ -274,7 +274,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const user = await req.server.services.scim.deleteScimUser({
|
||||
orgMembershipId: req.params.orgMembershipId,
|
||||
userId: req.params.userId,
|
||||
orgId: req.permission.orgId
|
||||
});
|
||||
|
||||
@@ -361,7 +361,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
handler: async (req) => {
|
||||
const groups = await req.server.services.scim.listScimGroups({
|
||||
orgId: req.permission.orgId,
|
||||
startIndex: req.query.startIndex,
|
||||
offset: req.query.startIndex,
|
||||
limit: req.query.count
|
||||
});
|
||||
|
||||
@@ -416,10 +416,10 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
displayName: z.string().trim(),
|
||||
members: z.array(
|
||||
z.object({
|
||||
value: z.string(), // infisical orgMembershipId
|
||||
value: z.string(), // infisical userId
|
||||
display: z.string()
|
||||
})
|
||||
)
|
||||
) // note: is this where members are added to group?
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@@ -534,11 +534,11 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/Users/:orgMembershipId",
|
||||
url: "/Users/:userId",
|
||||
method: "PUT",
|
||||
schema: {
|
||||
params: z.object({
|
||||
orgMembershipId: z.string().trim()
|
||||
userId: z.string().trim()
|
||||
}),
|
||||
body: z.object({
|
||||
schemas: z.array(z.string()),
|
||||
@@ -575,7 +575,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const user = await req.server.services.scim.replaceScimUser({
|
||||
orgMembershipId: req.params.orgMembershipId,
|
||||
userId: req.params.userId,
|
||||
orgId: req.permission.orgId,
|
||||
active: req.body.active
|
||||
});
|
||||
|
@@ -130,7 +130,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
approvals: sapPubSchema.merge(z.object({ approvers: z.string().array() })).array()
|
||||
approvals: sapPubSchema.merge(z.object({ approvers: z.string().nullish().array() })).array()
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -161,7 +161,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
policy: sapPubSchema.merge(z.object({ approvers: z.string().array() })).optional()
|
||||
policy: sapPubSchema.merge(z.object({ approvers: z.string().nullish().array() })).optional()
|
||||
})
|
||||
}
|
||||
},
|
||||
|
@@ -32,20 +32,22 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
approvals: SecretApprovalRequestsSchema.extend({
|
||||
// secretPath: z.string(),
|
||||
policy: z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
approvals: z.number(),
|
||||
approvers: z.string().array(),
|
||||
secretPath: z.string().optional().nullable()
|
||||
}),
|
||||
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
|
||||
environment: z.string(),
|
||||
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
|
||||
approvers: z.string().array()
|
||||
}).array()
|
||||
approvals: SecretApprovalRequestsSchema.merge(
|
||||
z.object({
|
||||
// secretPath: z.string(),
|
||||
policy: z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
approvals: z.number(),
|
||||
approvers: z.string().array(),
|
||||
secretPath: z.string().optional().nullable()
|
||||
}),
|
||||
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
|
||||
environment: z.string(),
|
||||
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
|
||||
approvers: z.string().array()
|
||||
})
|
||||
).array()
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -195,7 +197,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
||||
type: isClosing ? EventType.SECRET_APPROVAL_CLOSED : EventType.SECRET_APPROVAL_REOPENED,
|
||||
// eslint-disable-next-line
|
||||
metadata: {
|
||||
[isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]: approval.statusChangeBy as string,
|
||||
[isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]: approval.statusChangeByUserId as string,
|
||||
secretApprovalRequestId: approval.id,
|
||||
secretApprovalRequestSlug: approval.slug
|
||||
// eslint-disable-next-line
|
||||
|
@@ -20,7 +20,7 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
||||
`${TableName.AccessApprovalPolicy}.id`,
|
||||
`${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("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||
.select(tx.ref("id").withSchema(TableName.Environment).as("envId"))
|
||||
@@ -38,12 +38,12 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
||||
const formatedDoc = mergeOneToManyRelation(
|
||||
doc,
|
||||
"id",
|
||||
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||
({ approverUserId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||
...el,
|
||||
envId,
|
||||
environment: { id: envId, name, slug }
|
||||
}),
|
||||
({ approverId }) => approverId,
|
||||
({ approverUserId }) => approverUserId,
|
||||
"approvers"
|
||||
);
|
||||
return formatedDoc?.[0];
|
||||
@@ -58,12 +58,12 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
||||
const formatedDoc = mergeOneToManyRelation(
|
||||
docs,
|
||||
"id",
|
||||
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||
({ approverUserId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||
...el,
|
||||
envId,
|
||||
environment: { id: envId, name, slug }
|
||||
}),
|
||||
({ approverId }) => approverId,
|
||||
({ approverUserId }) => approverUserId,
|
||||
"approvers"
|
||||
);
|
||||
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 { TProjectDALFactory } from "@app/services/project/project-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 { TAccessApprovalPolicyDALFactory } from "./access-approval-policy-dal";
|
||||
@@ -24,7 +24,7 @@ type TSecretApprovalPolicyServiceFactoryDep = {
|
||||
accessApprovalPolicyDAL: TAccessApprovalPolicyDALFactory;
|
||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findOne">;
|
||||
accessApprovalPolicyApproverDAL: TAccessApprovalPolicyApproverDALFactory;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
|
||||
userDAL: Pick<TUserDALFactory, "findUsersByProjectId">;
|
||||
};
|
||||
|
||||
export type TAccessApprovalPolicyServiceFactory = ReturnType<typeof accessApprovalPolicyServiceFactory>;
|
||||
@@ -34,8 +34,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
accessApprovalPolicyApproverDAL,
|
||||
permissionService,
|
||||
projectEnvDAL,
|
||||
projectDAL,
|
||||
projectMembershipDAL
|
||||
userDAL,
|
||||
projectDAL
|
||||
}: TSecretApprovalPolicyServiceFactoryDep) => {
|
||||
const createAccessApprovalPolicy = async ({
|
||||
name,
|
||||
@@ -69,12 +69,13 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
const env = await projectEnvDAL.findOne({ slug: environment, projectId: project.id });
|
||||
if (!env) throw new BadRequestError({ message: "Environment not found" });
|
||||
|
||||
const secretApprovers = await projectMembershipDAL.find({
|
||||
projectId: project.id,
|
||||
$in: { id: approvers }
|
||||
});
|
||||
// We need to get the users by project ID to ensure they are part of the project.
|
||||
const accessApproverUsers = await userDAL.findUsersByProjectId(
|
||||
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" });
|
||||
}
|
||||
|
||||
@@ -85,7 +86,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
secretPath,
|
||||
actorAuthMethod,
|
||||
permissionService,
|
||||
userIds: secretApprovers.map((approver) => approver.userId)
|
||||
userIds: accessApproverUsers.map((user) => user.id)
|
||||
});
|
||||
|
||||
const accessApproval = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
||||
@@ -99,8 +100,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
tx
|
||||
);
|
||||
await accessApprovalPolicyApproverDAL.insertMany(
|
||||
secretApprovers.map(({ id }) => ({
|
||||
approverId: id,
|
||||
accessApproverUsers.map((user) => ({
|
||||
approverUserId: user.id,
|
||||
policyId: doc.id
|
||||
})),
|
||||
tx
|
||||
@@ -169,12 +170,9 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
);
|
||||
if (approvers) {
|
||||
// Find the workspace project memberships of the users passed in the approvers array
|
||||
const secretApprovers = await projectMembershipDAL.find(
|
||||
{
|
||||
projectId: accessApprovalPolicy.projectId,
|
||||
$in: { id: approvers }
|
||||
},
|
||||
{ tx }
|
||||
const secretApproverUsers = await userDAL.findUsersByProjectId(
|
||||
accessApprovalPolicy.projectId,
|
||||
approvers.map((approverUserId) => approverUserId)
|
||||
);
|
||||
|
||||
await verifyApprovers({
|
||||
@@ -184,15 +182,15 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
secretPath: doc.secretPath!,
|
||||
actorAuthMethod,
|
||||
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" });
|
||||
await accessApprovalPolicyApproverDAL.delete({ policyId: doc.id }, tx);
|
||||
await accessApprovalPolicyApproverDAL.insertMany(
|
||||
secretApprovers.map(({ id }) => ({
|
||||
approverId: id,
|
||||
secretApproverUsers.map((user) => ({
|
||||
approverUserId: user.id,
|
||||
policyId: doc.id
|
||||
})),
|
||||
tx
|
||||
|
@@ -11,6 +11,42 @@ export type TAccessApprovalRequestDALFactory = ReturnType<typeof accessApprovalR
|
||||
|
||||
export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
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[]) => {
|
||||
try {
|
||||
@@ -19,9 +55,14 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
|
||||
.leftJoin(
|
||||
TableName.ProjectUserAdditionalPrivilege,
|
||||
`${TableName.AccessApprovalRequest}.privilegeId`,
|
||||
`${TableName.AccessApprovalRequest}.projectUserPrivilegeId`,
|
||||
`${TableName.ProjectUserAdditionalPrivilege}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.GroupProjectUserAdditionalPrivilege,
|
||||
`${TableName.AccessApprovalRequest}.groupProjectUserPrivilegeId`,
|
||||
`${TableName.GroupProjectUserAdditionalPrivilege}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.AccessApprovalPolicy,
|
||||
`${TableName.AccessApprovalRequest}.policyId`,
|
||||
@@ -50,7 +91,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
db.ref("envId").withSchema(TableName.AccessApprovalPolicy).as("policyEnvId")
|
||||
)
|
||||
|
||||
.select(db.ref("approverId").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||
.select(db.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||
|
||||
.select(
|
||||
db.ref("projectId").withSchema(TableName.Environment),
|
||||
@@ -59,32 +100,85 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
)
|
||||
|
||||
.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")
|
||||
)
|
||||
|
||||
// Project user additional privilege
|
||||
.select(
|
||||
db
|
||||
.ref("projectMembershipId")
|
||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||
.as("privilegeMembershipId"),
|
||||
db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeIsTemporary"),
|
||||
db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeTemporaryMode"),
|
||||
db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeTemporaryRange"),
|
||||
.as("projectPrivilegeProjectMembershipId"),
|
||||
|
||||
db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("projectPrivilegeIsTemporary"),
|
||||
|
||||
db
|
||||
.ref("temporaryMode")
|
||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||
.as("projectPrivilegeTemporaryMode"),
|
||||
|
||||
db
|
||||
.ref("temporaryRange")
|
||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||
.as("projectPrivilegeTemporaryRange"),
|
||||
|
||||
db
|
||||
.ref("temporaryAccessStartTime")
|
||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
||||
.as("privilegeTemporaryAccessStartTime"),
|
||||
.as("projectPrivilegeTemporaryAccessStartTime"),
|
||||
|
||||
db
|
||||
.ref("temporaryAccessEndTime")
|
||||
.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");
|
||||
|
||||
const formattedDocs = sqlNestRelationships({
|
||||
const projectUserFormattedDocs = sqlNestRelationships({
|
||||
data: docs,
|
||||
key: "id",
|
||||
parentMapper: (doc) => ({
|
||||
@@ -99,33 +193,49 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
secretPath: doc.policySecretPath,
|
||||
envId: doc.policyEnvId
|
||||
},
|
||||
privilege: doc.privilegeId
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
privilege: doc.projectUserPrivilegeId
|
||||
? {
|
||||
membershipId: doc.privilegeMembershipId,
|
||||
isTemporary: doc.privilegeIsTemporary,
|
||||
temporaryMode: doc.privilegeTemporaryMode,
|
||||
temporaryRange: doc.privilegeTemporaryRange,
|
||||
temporaryAccessStartTime: doc.privilegeTemporaryAccessStartTime,
|
||||
temporaryAccessEndTime: doc.privilegeTemporaryAccessEndTime,
|
||||
permissions: doc.privilegePermissions
|
||||
projectMembershipId: doc.projectMembershipId,
|
||||
groupMembershipId: null,
|
||||
requestedByUserId: null,
|
||||
isTemporary: doc.projectPrivilegeIsTemporary,
|
||||
temporaryMode: doc.projectPrivilegeTemporaryMode,
|
||||
temporaryRange: doc.projectPrivilegeTemporaryRange,
|
||||
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: [
|
||||
{
|
||||
key: "reviewerMemberId",
|
||||
key: "reviewerUserId",
|
||||
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,
|
||||
policy: { ...doc.policy, approvers: doc.approvers }
|
||||
}));
|
||||
@@ -157,7 +267,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
||||
.select(selectAllTableCols(TableName.AccessApprovalRequest))
|
||||
.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("id").withSchema(TableName.AccessApprovalPolicy).as("policyId"),
|
||||
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("secretPath").withSchema(TableName.AccessApprovalPolicy).as("policySecretPath"),
|
||||
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) => {
|
||||
@@ -188,11 +298,12 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
}),
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "reviewerMemberId",
|
||||
key: "reviewerUserId",
|
||||
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;
|
||||
@@ -214,12 +325,6 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
`${TableName.AccessApprovalPolicy}.id`
|
||||
)
|
||||
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
||||
.leftJoin(
|
||||
TableName.ProjectUserAdditionalPrivilege,
|
||||
`${TableName.AccessApprovalRequest}.privilegeId`,
|
||||
`${TableName.ProjectUserAdditionalPrivilege}.id`
|
||||
)
|
||||
|
||||
.leftJoin(
|
||||
TableName.AccessApprovalRequestReviewer,
|
||||
`${TableName.AccessApprovalRequest}.id`,
|
||||
@@ -229,7 +334,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
.where(`${TableName.Environment}.projectId`, projectId)
|
||||
.select(selectAllTableCols(TableName.AccessApprovalRequest))
|
||||
.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({
|
||||
data: accessRequests,
|
||||
@@ -239,21 +344,28 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
}),
|
||||
childrenMapper: [
|
||||
{
|
||||
key: "reviewerMemberId",
|
||||
key: "memberUserId",
|
||||
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
|
||||
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
|
||||
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 };
|
||||
@@ -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 ms from "ms";
|
||||
|
||||
import { ProjectMembershipRole } from "@app/db/schemas";
|
||||
import { ProjectMembershipRole, TProjectUserAdditionalPrivilege } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
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 { TAccessApprovalPolicyDALFactory } from "../access-approval-policy/access-approval-policy-dal";
|
||||
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 { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||
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 { TAccessApprovalRequestDALFactory } from "./access-approval-request-dal";
|
||||
@@ -23,13 +26,15 @@ import { TAccessApprovalRequestReviewerDALFactory } from "./access-approval-requ
|
||||
import {
|
||||
ApprovalStatus,
|
||||
TCreateAccessApprovalRequestDTO,
|
||||
TDeleteApprovalRequestDTO,
|
||||
TGetAccessRequestCountDTO,
|
||||
TListApprovalRequestsDTO,
|
||||
TReviewAccessRequestDTO
|
||||
} from "./access-approval-request-types";
|
||||
|
||||
type TSecretApprovalRequestServiceFactoryDep = {
|
||||
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "create" | "findById">;
|
||||
type TAccessApprovalRequestServiceFactoryDep = {
|
||||
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "create" | "findById" | "deleteById">;
|
||||
groupAdditionalPrivilegeDAL: TGroupProjectUserAdditionalPrivilegeDALFactory;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||
accessApprovalPolicyApproverDAL: Pick<TAccessApprovalPolicyApproverDALFactory, "find">;
|
||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
||||
@@ -44,6 +49,7 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
||||
| "updateById"
|
||||
| "findOne"
|
||||
| "getCount"
|
||||
| "deleteById"
|
||||
>;
|
||||
accessApprovalPolicyDAL: Pick<TAccessApprovalPolicyDALFactory, "findOne" | "find">;
|
||||
accessApprovalRequestReviewerDAL: Pick<
|
||||
@@ -52,7 +58,10 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
||||
>;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findById">;
|
||||
smtpService: Pick<TSmtpService, "sendMail">;
|
||||
userDAL: Pick<TUserDALFactory, "findUserByProjectMembershipId" | "findUsersByProjectMembershipIds">;
|
||||
userDAL: Pick<
|
||||
TUserDALFactory,
|
||||
"findUserByProjectMembershipId" | "findUsersByProjectMembershipIds" | "findUsersByProjectId" | "findUserByProjectId"
|
||||
>;
|
||||
};
|
||||
|
||||
export type TAccessApprovalRequestServiceFactory = ReturnType<typeof accessApprovalRequestServiceFactory>;
|
||||
@@ -62,6 +71,7 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
projectEnvDAL,
|
||||
permissionService,
|
||||
accessApprovalRequestDAL,
|
||||
groupAdditionalPrivilegeDAL,
|
||||
accessApprovalRequestReviewerDAL,
|
||||
projectMembershipDAL,
|
||||
accessApprovalPolicyDAL,
|
||||
@@ -69,7 +79,7 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
additionalPrivilegeDAL,
|
||||
smtpService,
|
||||
userDAL
|
||||
}: TSecretApprovalRequestServiceFactoryDep) => {
|
||||
}: TAccessApprovalRequestServiceFactoryDep) => {
|
||||
const createAccessApprovalRequest = async ({
|
||||
isTemporary,
|
||||
temporaryRange,
|
||||
@@ -94,9 +104,6 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
);
|
||||
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);
|
||||
|
||||
const { envSlug, secretPath, accessTypes } = verifyRequestedPermissions({ permissions: requestedPermissions });
|
||||
@@ -114,25 +121,43 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
policyId: policy.id
|
||||
});
|
||||
|
||||
const approverUsers = await userDAL.findUsersByProjectMembershipIds(
|
||||
approvers.map((approver) => approver.approverId)
|
||||
if (approvers.some((approver) => !approver.approverUserId)) {
|
||||
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({
|
||||
policyId: policy.id,
|
||||
requestedBy: membership.id,
|
||||
requestedByUserId: actorId,
|
||||
permissions: JSON.stringify(requestedPermissions),
|
||||
isTemporary
|
||||
});
|
||||
|
||||
if (duplicateRequests?.length > 0) {
|
||||
for await (const duplicateRequest of duplicateRequests) {
|
||||
if (duplicateRequest.privilegeId) {
|
||||
const privilege = await additionalPrivilegeDAL.findById(duplicateRequest.privilegeId);
|
||||
let foundPrivilege: Pick<
|
||||
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" });
|
||||
}
|
||||
} else {
|
||||
@@ -150,10 +175,18 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
}
|
||||
|
||||
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(
|
||||
{
|
||||
projectMembershipId: requesterUser.projectMembershipId || null,
|
||||
groupMembershipId: requesterUser.groupProjectMembershipId || null,
|
||||
policyId: policy.id,
|
||||
requestedBy: membership.id,
|
||||
requestedByUserId: actorId, // This is the user ID of the person who made the request
|
||||
temporaryRange: temporaryRange || null,
|
||||
permissions: JSON.stringify(requestedPermissions),
|
||||
isTemporary
|
||||
@@ -187,9 +220,62 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
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 ({
|
||||
projectSlug,
|
||||
authorProjectMembershipId,
|
||||
authorUserId,
|
||||
envSlug,
|
||||
actor,
|
||||
actorOrgId,
|
||||
@@ -211,13 +297,8 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
const policies = await accessApprovalPolicyDAL.find({ projectId: project.id });
|
||||
let requests = await accessApprovalRequestDAL.findRequestsWithPrivilegeByPolicyIds(policies.map((p) => p.id));
|
||||
|
||||
if (authorProjectMembershipId) {
|
||||
requests = requests.filter((request) => request.requestedBy === authorProjectMembershipId);
|
||||
}
|
||||
|
||||
if (envSlug) {
|
||||
requests = requests.filter((request) => request.environment === envSlug);
|
||||
}
|
||||
if (authorUserId) requests = requests.filter((request) => request.requestedByUserId === authorUserId);
|
||||
if (envSlug) requests = requests.filter((request) => request.environment === envSlug);
|
||||
|
||||
return { requests };
|
||||
};
|
||||
@@ -246,8 +327,8 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
|
||||
if (
|
||||
!hasRole(ProjectMembershipRole.Admin) &&
|
||||
accessApprovalRequest.requestedBy !== membership.id && // 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
|
||||
accessApprovalRequest.requestedByUserId !== actorId && // The request wasn't made by the current user
|
||||
!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" });
|
||||
}
|
||||
@@ -273,7 +354,7 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
const review = await accessApprovalRequestReviewerDAL.findOne(
|
||||
{
|
||||
requestId: accessApprovalRequest.id,
|
||||
member: membership.id
|
||||
memberUserId: actorId
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -282,7 +363,7 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
{
|
||||
status,
|
||||
requestId: accessApprovalRequest.id,
|
||||
member: membership.id
|
||||
memberUserId: actorId
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -297,41 +378,92 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
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) {
|
||||
// Permanent access
|
||||
const privilege = await additionalPrivilegeDAL.create(
|
||||
{
|
||||
projectMembershipId: accessApprovalRequest.requestedBy,
|
||||
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
||||
permissions: JSON.stringify(accessApprovalRequest.permissions)
|
||||
},
|
||||
tx
|
||||
);
|
||||
privilegeId = privilege.id;
|
||||
if (accessApprovalRequest.groupMembershipId) {
|
||||
// Group user privilege
|
||||
const groupProjectUserAdditionalPrivilege = await groupAdditionalPrivilegeDAL.create(
|
||||
{
|
||||
groupProjectMembershipId: accessApprovalRequest.groupMembershipId,
|
||||
requestedByUserId: accessApprovalRequest.requestedByUserId,
|
||||
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
||||
permissions: JSON.stringify(accessApprovalRequest.permissions)
|
||||
},
|
||||
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 {
|
||||
// Temporary access
|
||||
const relativeTempAllocatedTimeInMs = ms(accessApprovalRequest.temporaryRange!);
|
||||
const startTime = new Date();
|
||||
|
||||
const privilege = await additionalPrivilegeDAL.create(
|
||||
{
|
||||
projectMembershipId: accessApprovalRequest.requestedBy,
|
||||
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
|
||||
);
|
||||
privilegeId = privilege.id;
|
||||
if (accessApprovalRequest.groupMembershipId) {
|
||||
// Group user privilege
|
||||
const groupProjectUserAdditionalPrivilege = await groupAdditionalPrivilegeDAL.create(
|
||||
{
|
||||
groupProjectMembershipId: accessApprovalRequest.groupMembershipId,
|
||||
requestedByUserId: accessApprovalRequest.requestedByUserId,
|
||||
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
|
||||
);
|
||||
|
||||
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;
|
||||
@@ -364,6 +496,7 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
createAccessApprovalRequest,
|
||||
listApprovalRequests,
|
||||
reviewAccessRequest,
|
||||
deleteAccessApprovalRequest,
|
||||
getCount
|
||||
};
|
||||
};
|
||||
|
@@ -28,6 +28,11 @@ export type TCreateAccessApprovalRequestDTO = {
|
||||
|
||||
export type TListApprovalRequestsDTO = {
|
||||
projectSlug: string;
|
||||
authorProjectMembershipId?: string;
|
||||
authorUserId?: string;
|
||||
envSlug?: string;
|
||||
} & 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 { request } from "@app/lib/config/request";
|
||||
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
||||
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 {
|
||||
pushToLog
|
||||
pushToLog,
|
||||
startAuditLogPruneJob
|
||||
};
|
||||
};
|
||||
|
@@ -51,7 +51,6 @@ export enum EventType {
|
||||
UNAUTHORIZE_INTEGRATION = "unauthorize-integration",
|
||||
CREATE_INTEGRATION = "create-integration",
|
||||
DELETE_INTEGRATION = "delete-integration",
|
||||
MANUAL_SYNC_INTEGRATION = "manual-sync-integration",
|
||||
ADD_TRUSTED_IP = "add-trusted-ip",
|
||||
UPDATE_TRUSTED_IP = "update-trusted-ip",
|
||||
DELETE_TRUSTED_IP = "delete-trusted-ip",
|
||||
@@ -64,25 +63,9 @@ export enum EventType {
|
||||
ADD_IDENTITY_UNIVERSAL_AUTH = "add-identity-universal-auth",
|
||||
UPDATE_IDENTITY_UNIVERSAL_AUTH = "update-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",
|
||||
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-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",
|
||||
UPDATE_ENVIRONMENT = "update-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 {
|
||||
type: EventType.ADD_TRUSTED_IP;
|
||||
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 {
|
||||
type: EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET;
|
||||
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 {
|
||||
type: EventType.CREATE_ENVIRONMENT;
|
||||
metadata: {
|
||||
@@ -837,9 +625,9 @@ interface SecretApprovalReopened {
|
||||
interface SecretApprovalRequest {
|
||||
type: EventType.SECRET_APPROVAL_REQUEST;
|
||||
metadata: {
|
||||
committedBy: string;
|
||||
secretApprovalRequestSlug: string;
|
||||
secretApprovalRequestId: string;
|
||||
committedByUser?: string | null; // Needs to be nullable for backward compatibility
|
||||
};
|
||||
}
|
||||
|
||||
@@ -857,7 +645,6 @@ export type Event =
|
||||
| UnauthorizeIntegrationEvent
|
||||
| CreateIntegrationEvent
|
||||
| DeleteIntegrationEvent
|
||||
| ManualSyncIntegrationEvent
|
||||
| AddTrustedIPEvent
|
||||
| UpdateTrustedIPEvent
|
||||
| DeleteTrustedIPEvent
|
||||
@@ -870,25 +657,9 @@ export type Event =
|
||||
| AddIdentityUniversalAuthEvent
|
||||
| UpdateIdentityUniversalAuthEvent
|
||||
| GetIdentityUniversalAuthEvent
|
||||
| LoginIdentityKubernetesAuthEvent
|
||||
| AddIdentityKubernetesAuthEvent
|
||||
| UpdateIdentityKubernetesAuthEvent
|
||||
| GetIdentityKubernetesAuthEvent
|
||||
| CreateIdentityUniversalAuthClientSecretEvent
|
||||
| GetIdentityUniversalAuthClientSecretsEvent
|
||||
| RevokeIdentityUniversalAuthClientSecretEvent
|
||||
| LoginIdentityGcpAuthEvent
|
||||
| AddIdentityGcpAuthEvent
|
||||
| UpdateIdentityGcpAuthEvent
|
||||
| GetIdentityGcpAuthEvent
|
||||
| LoginIdentityAwsAuthEvent
|
||||
| AddIdentityAwsAuthEvent
|
||||
| UpdateIdentityAwsAuthEvent
|
||||
| GetIdentityAwsAuthEvent
|
||||
| LoginIdentityAzureAuthEvent
|
||||
| AddIdentityAzureAuthEvent
|
||||
| UpdateIdentityAzureAuthEvent
|
||||
| GetIdentityAzureAuthEvent
|
||||
| CreateEnvironmentEvent
|
||||
| UpdateEnvironmentEvent
|
||||
| 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 { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt } from "@app/lib/knex";
|
||||
|
||||
import { TUserGroupMembershipDALFactory } from "./user-group-membership-dal";
|
||||
|
||||
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 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> = {}) => {
|
||||
try {
|
||||
@@ -122,9 +190,10 @@ export const groupDALFactory = (db: TDbClient) => {
|
||||
};
|
||||
|
||||
return {
|
||||
...groupOrm,
|
||||
findGroups,
|
||||
findByOrgId,
|
||||
findAllGroupMembers,
|
||||
...groupOrm
|
||||
delete: deleteMany
|
||||
};
|
||||
};
|
||||
|
@@ -1,6 +1,6 @@
|
||||
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 { BadRequestError, ScimRequestError } from "@app/lib/errors";
|
||||
|
||||
@@ -188,9 +188,9 @@ export const addUsersToGroupByUserIds = async ({
|
||||
// check if all user(s) are part of the organization
|
||||
const existingUserOrgMemberships = await orgDAL.findMembership(
|
||||
{
|
||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: group.orgId,
|
||||
orgId: group.orgId,
|
||||
$in: {
|
||||
[`${TableName.OrgMembership}.userId` as "userId"]: userIds
|
||||
userId: userIds
|
||||
}
|
||||
},
|
||||
{ tx }
|
||||
@@ -266,6 +266,9 @@ export const removeUsersFromGroupByUserIds = async ({
|
||||
userIds,
|
||||
userDAL,
|
||||
userGroupMembershipDAL,
|
||||
accessApprovalRequestDAL,
|
||||
secretApprovalRequestDAL,
|
||||
secretApprovalPolicyDAL,
|
||||
groupProjectDAL,
|
||||
projectKeyDAL,
|
||||
tx: outerTx
|
||||
@@ -322,20 +325,16 @@ export const removeUsersFromGroupByUserIds = async ({
|
||||
});
|
||||
|
||||
if (membersToRemoveFromGroupNonPending.length) {
|
||||
// check which projects the group is part of
|
||||
const projectIds = Array.from(
|
||||
new Set(
|
||||
(
|
||||
await groupProjectDAL.find(
|
||||
{
|
||||
groupId: group.id
|
||||
},
|
||||
{ tx }
|
||||
)
|
||||
).map((gp) => gp.projectId)
|
||||
)
|
||||
const groupProjectMemberships = await groupProjectDAL.find(
|
||||
{
|
||||
groupId: group.id
|
||||
},
|
||||
{ tx }
|
||||
);
|
||||
|
||||
// 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
|
||||
for await (const userId of userIds) {
|
||||
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(
|
||||
{
|
||||
groupId: group.id,
|
||||
userId
|
||||
$in: {
|
||||
userId: membersToRemoveFromGroupNonPending.map((member) => member.id)
|
||||
}
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -364,12 +388,15 @@ export const removeUsersFromGroupByUserIds = async ({
|
||||
}
|
||||
|
||||
if (membersToRemoveFromGroupPending.length) {
|
||||
await userGroupMembershipDAL.delete({
|
||||
groupId: group.id,
|
||||
$in: {
|
||||
userId: membersToRemoveFromGroupPending.map((member) => member.id)
|
||||
}
|
||||
});
|
||||
await userGroupMembershipDAL.delete(
|
||||
{
|
||||
groupId: group.id,
|
||||
$in: {
|
||||
userId: membersToRemoveFromGroupPending.map((member) => member.id)
|
||||
}
|
||||
},
|
||||
tx
|
||||
);
|
||||
}
|
||||
|
||||
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 { TUserDALFactory } from "@app/services/user/user-dal";
|
||||
|
||||
import { TAccessApprovalRequestDALFactory } from "../access-approval-request/access-approval-request-dal";
|
||||
import { TLicenseServiceFactory } from "../license/license-service";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||
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 { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "./group-fns";
|
||||
import {
|
||||
@@ -41,6 +44,9 @@ type TGroupServiceFactoryDep = {
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "findLatestProjectKey" | "insertMany">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRole">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "delete">;
|
||||
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "delete">;
|
||||
secretApprovalPolicyDAL: Pick<TSecretApprovalPolicyDALFactory, "findByProjectIds">;
|
||||
};
|
||||
|
||||
export type TGroupServiceFactory = ReturnType<typeof groupServiceFactory>;
|
||||
@@ -50,6 +56,9 @@ export const groupServiceFactory = ({
|
||||
groupDAL,
|
||||
groupProjectDAL,
|
||||
orgDAL,
|
||||
secretApprovalRequestDAL,
|
||||
secretApprovalPolicyDAL,
|
||||
accessApprovalRequestDAL,
|
||||
userGroupMembershipDAL,
|
||||
projectDAL,
|
||||
projectBotDAL,
|
||||
@@ -328,6 +337,9 @@ export const groupServiceFactory = ({
|
||||
group,
|
||||
userIds: [user.id],
|
||||
userDAL,
|
||||
accessApprovalRequestDAL,
|
||||
secretApprovalPolicyDAL,
|
||||
secretApprovalRequestDAL,
|
||||
userGroupMembershipDAL,
|
||||
groupProjectDAL,
|
||||
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 { 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 = {
|
||||
name: string;
|
||||
slug?: string;
|
||||
@@ -77,6 +81,9 @@ export type TRemoveUsersFromGroupByUserIds = {
|
||||
group: TGroups;
|
||||
userIds: string[];
|
||||
userDAL: Pick<TUserDALFactory, "find" | "transaction">;
|
||||
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "delete">;
|
||||
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "delete">;
|
||||
secretApprovalPolicyDAL: Pick<TSecretApprovalPolicyDALFactory, "findByProjectIds">;
|
||||
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "find" | "filterProjectsByUserMembership" | "delete">;
|
||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "delete">;
|
||||
|
@@ -1,7 +1,5 @@
|
||||
import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
|
||||
import { PackRule, unpackRules } from "@casl/ability/extra";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import ms from "ms";
|
||||
import { z } from "zod";
|
||||
|
||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||
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 { 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 {
|
||||
IdentityProjectAdditionalPrivilegeTemporaryMode,
|
||||
@@ -32,27 +30,6 @@ export type TIdentityProjectAdditionalPrivilegeServiceFactory = ReturnType<
|
||||
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 = ({
|
||||
identityProjectAdditionalPrivilegeDAL,
|
||||
identityProjectDAL,
|
||||
@@ -109,10 +86,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
||||
slug,
|
||||
permissions: customPermission
|
||||
});
|
||||
return {
|
||||
...additionalPrivilege,
|
||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
||||
};
|
||||
return additionalPrivilege;
|
||||
}
|
||||
|
||||
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
|
||||
@@ -126,10 +100,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
||||
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
|
||||
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
|
||||
});
|
||||
return {
|
||||
...additionalPrivilege,
|
||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
||||
};
|
||||
return additionalPrivilege;
|
||||
};
|
||||
|
||||
const updateBySlug = async ({
|
||||
@@ -192,11 +163,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
||||
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
|
||||
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
|
||||
});
|
||||
return {
|
||||
...additionalPrivilege,
|
||||
|
||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
||||
};
|
||||
return additionalPrivilege;
|
||||
}
|
||||
|
||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
|
||||
@@ -207,11 +174,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
||||
temporaryRange: null,
|
||||
temporaryMode: null
|
||||
});
|
||||
return {
|
||||
...additionalPrivilege,
|
||||
|
||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
||||
};
|
||||
return additionalPrivilege;
|
||||
};
|
||||
|
||||
const deleteBySlug = async ({
|
||||
@@ -257,11 +220,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
||||
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
||||
|
||||
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
|
||||
return {
|
||||
...deletedPrivilege,
|
||||
|
||||
permissions: unpackPermissions(deletedPrivilege.permissions)
|
||||
};
|
||||
return deletedPrivilege;
|
||||
};
|
||||
|
||||
const getPrivilegeDetailsBySlug = async ({
|
||||
@@ -295,10 +254,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
||||
});
|
||||
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
||||
|
||||
return {
|
||||
...identityPrivilege,
|
||||
permissions: unpackPermissions(identityPrivilege.permissions)
|
||||
};
|
||||
return identityPrivilege;
|
||||
};
|
||||
|
||||
const listIdentityProjectPrivileges = async ({
|
||||
@@ -328,11 +284,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
||||
const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find({
|
||||
projectMembershipId: identityProjectMembership.id
|
||||
});
|
||||
return identityPrivileges.map((el) => ({
|
||||
...el,
|
||||
|
||||
permissions: unpackPermissions(el.permissions)
|
||||
}));
|
||||
return identityPrivileges;
|
||||
};
|
||||
|
||||
return {
|
||||
|
@@ -1,14 +1,7 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
import {
|
||||
OrgMembershipRole,
|
||||
OrgMembershipStatus,
|
||||
SecretKeyEncoding,
|
||||
TableName,
|
||||
TLdapConfigsUpdate,
|
||||
TUsers
|
||||
} from "@app/db/schemas";
|
||||
import { OrgMembershipRole, OrgMembershipStatus, SecretKeyEncoding, TLdapConfigsUpdate } from "@app/db/schemas";
|
||||
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
||||
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
|
||||
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 { TOrgBotDALFactory } from "@app/services/org/org-bot-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 { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-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 { 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 { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||
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 {
|
||||
TCreateLdapCfgDTO,
|
||||
@@ -56,7 +49,6 @@ import { TLdapGroupMapDALFactory } from "./ldap-group-map-dal";
|
||||
type TLdapConfigServiceFactoryDep = {
|
||||
ldapConfigDAL: Pick<TLdapConfigDALFactory, "create" | "update" | "findOne">;
|
||||
ldapGroupMapDAL: Pick<TLdapGroupMapDALFactory, "find" | "create" | "delete" | "findLdapGroupMapsByLdapConfigId">;
|
||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">;
|
||||
orgDAL: Pick<
|
||||
TOrgDALFactory,
|
||||
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
|
||||
@@ -78,6 +70,9 @@ type TLdapConfigServiceFactoryDep = {
|
||||
userAliasDAL: Pick<TUserAliasDALFactory, "create" | "findOne">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "delete">;
|
||||
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "delete">;
|
||||
secretApprovalPolicyDAL: Pick<TSecretApprovalPolicyDALFactory, "findByProjectIds">;
|
||||
};
|
||||
|
||||
export type TLdapConfigServiceFactory = ReturnType<typeof ldapConfigServiceFactory>;
|
||||
@@ -86,10 +81,12 @@ export const ldapConfigServiceFactory = ({
|
||||
ldapConfigDAL,
|
||||
ldapGroupMapDAL,
|
||||
orgDAL,
|
||||
orgMembershipDAL,
|
||||
orgBotDAL,
|
||||
groupDAL,
|
||||
groupProjectDAL,
|
||||
accessApprovalRequestDAL,
|
||||
secretApprovalPolicyDAL,
|
||||
secretApprovalRequestDAL,
|
||||
projectKeyDAL,
|
||||
projectDAL,
|
||||
projectBotDAL,
|
||||
@@ -391,17 +388,16 @@ export const ldapConfigServiceFactory = ({
|
||||
username,
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
emails,
|
||||
groups,
|
||||
orgId,
|
||||
relayState
|
||||
}: TLdapLoginDTO) => {
|
||||
const appCfg = getConfig();
|
||||
const serverCfg = await getServerCfg();
|
||||
let userAlias = await userAliasDAL.findOne({
|
||||
externalId,
|
||||
orgId,
|
||||
aliasType: UserAliasType.LDAP
|
||||
aliasType: AuthMethod.LDAP
|
||||
});
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
@@ -409,13 +405,7 @@ export const ldapConfigServiceFactory = ({
|
||||
|
||||
if (userAlias) {
|
||||
await userDAL.transaction(async (tx) => {
|
||||
const [orgMembership] = await orgDAL.findMembership(
|
||||
{
|
||||
[`${TableName.OrgMembership}.userId` as "userId"]: userAlias.userId,
|
||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||
},
|
||||
{ tx }
|
||||
);
|
||||
const [orgMembership] = await orgDAL.findMembership({ userId: userAlias.userId }, { tx });
|
||||
if (!orgMembership) {
|
||||
await orgDAL.createMembership(
|
||||
{
|
||||
@@ -438,75 +428,40 @@ export const ldapConfigServiceFactory = ({
|
||||
});
|
||||
} else {
|
||||
userAlias = await userDAL.transaction(async (tx) => {
|
||||
let newUser: TUsers | undefined;
|
||||
if (serverCfg.trustSamlEmails) {
|
||||
newUser = await userDAL.findOne(
|
||||
{
|
||||
email,
|
||||
isEmailVerified: true
|
||||
},
|
||||
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 uniqueUsername = await normalizeUsername(username, userDAL);
|
||||
const newUser = await userDAL.create(
|
||||
{
|
||||
username: uniqueUsername,
|
||||
email: emails[0],
|
||||
firstName,
|
||||
lastName,
|
||||
authMethods: [AuthMethod.LDAP],
|
||||
isGhost: false
|
||||
},
|
||||
tx
|
||||
);
|
||||
const newUserAlias = await userAliasDAL.create(
|
||||
{
|
||||
userId: newUser.id,
|
||||
username,
|
||||
aliasType: UserAliasType.LDAP,
|
||||
aliasType: AuthMethod.LDAP,
|
||||
externalId,
|
||||
emails: [email],
|
||||
emails,
|
||||
orgId
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const [orgMembership] = await orgDAL.findMembership(
|
||||
await orgDAL.createMembership(
|
||||
{
|
||||
[`${TableName.OrgMembership}.userId` as "userId"]: newUser.id,
|
||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||
userId: newUser.id,
|
||||
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;
|
||||
});
|
||||
}
|
||||
@@ -578,7 +533,10 @@ export const ldapConfigServiceFactory = ({
|
||||
group,
|
||||
userIds: [newUser.id],
|
||||
userDAL,
|
||||
secretApprovalRequestDAL,
|
||||
accessApprovalRequestDAL,
|
||||
userGroupMembershipDAL,
|
||||
secretApprovalPolicyDAL,
|
||||
groupProjectDAL,
|
||||
projectKeyDAL,
|
||||
tx
|
||||
@@ -597,14 +555,11 @@ export const ldapConfigServiceFactory = ({
|
||||
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
||||
userId: user.id,
|
||||
username: user.username,
|
||||
...(user.email && { email: user.email, isEmailVerified: user.isEmailVerified }),
|
||||
firstName,
|
||||
lastName,
|
||||
organizationName: organization.name,
|
||||
organizationId: organization.id,
|
||||
organizationSlug: organization.slug,
|
||||
authMethod: AuthMethod.LDAP,
|
||||
authType: UserAliasType.LDAP,
|
||||
isUserCompleted,
|
||||
...(relayState
|
||||
? {
|
||||
|
@@ -51,7 +51,7 @@ export type TLdapLoginDTO = {
|
||||
username: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
emails: string[];
|
||||
orgId: string;
|
||||
groups?: {
|
||||
dn: string;
|
||||
|
@@ -16,8 +16,6 @@ export const licenseDALFactory = (db: TDbClient) => {
|
||||
void bd.where({ orgId });
|
||||
}
|
||||
})
|
||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||
.where(`${TableName.Users}.isGhost`, false)
|
||||
.count();
|
||||
return doc?.[0].count;
|
||||
} catch (error) {
|
||||
|
@@ -62,6 +62,11 @@ export const permissionDALFactory = (db: TDbClient) => {
|
||||
`${TableName.GroupProjectMembershipRole}.projectMembershipId`,
|
||||
`${TableName.GroupProjectMembership}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.GroupProjectUserAdditionalPrivilege,
|
||||
`${TableName.GroupProjectUserAdditionalPrivilege}.groupProjectMembershipId`,
|
||||
`${TableName.GroupProjectMembership}.id`
|
||||
)
|
||||
.leftJoin(
|
||||
TableName.ProjectRoles,
|
||||
`${TableName.GroupProjectMembershipRole}.customRoleId`,
|
||||
@@ -77,11 +82,34 @@ export const permissionDALFactory = (db: TDbClient) => {
|
||||
db.ref("projectId").withSchema(TableName.GroupProjectMembership),
|
||||
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
|
||||
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(
|
||||
TableName.ProjectUserMembershipRole,
|
||||
`${TableName.ProjectUserMembershipRole}.projectMembershipId`,
|
||||
@@ -127,7 +155,7 @@ export const permissionDALFactory = (db: TDbClient) => {
|
||||
);
|
||||
|
||||
const permission = sqlNestRelationships({
|
||||
data: docs,
|
||||
data: projectMemberDocs,
|
||||
key: "projectId",
|
||||
parentMapper: ({ orgId, orgAuthEnforced, membershipId, membershipCreatedAt, membershipUpdatedAt }) => ({
|
||||
orgId,
|
||||
@@ -194,6 +222,33 @@ export const permissionDALFactory = (db: TDbClient) => {
|
||||
permissions: z.unknown(),
|
||||
customRoleSlug: z.string().optional().nullable()
|
||||
}).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)
|
||||
) ?? [];
|
||||
|
||||
const activeAdditionalPrivileges = permission?.[0]?.additionalPrivileges?.filter(
|
||||
({ isTemporary, temporaryAccessEndTime }) =>
|
||||
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
|
||||
);
|
||||
const activeAdditionalPrivileges =
|
||||
permission?.[0]?.additionalPrivileges?.filter(
|
||||
({ 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 {
|
||||
...(permission[0] || groupPermission[0]),
|
||||
roles: [...activeRoles, ...activeGroupRoles],
|
||||
additionalPrivileges: activeAdditionalPrivileges
|
||||
additionalPrivileges: [...activeAdditionalPrivileges, ...activeGroupAdditionalPrivileges]
|
||||
};
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "GetProjectPermission" });
|
||||
|
@@ -90,6 +90,10 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
||||
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
||||
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);
|
||||
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
|
||||
|
||||
@@ -138,6 +142,10 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
||||
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
||||
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);
|
||||
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
|
||||
|
||||
@@ -164,6 +172,10 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
||||
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
||||
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);
|
||||
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
|
||||
|
||||
|
@@ -7,8 +7,7 @@ import {
|
||||
SecretKeyEncoding,
|
||||
TableName,
|
||||
TSamlConfigs,
|
||||
TSamlConfigsUpdate,
|
||||
TUsers
|
||||
TSamlConfigsUpdate
|
||||
} from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import {
|
||||
@@ -20,18 +19,10 @@ import {
|
||||
infisicalSymmetricEncypt
|
||||
} from "@app/lib/crypto/encryption";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { 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 { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
||||
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
|
||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||
import { 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 { 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 { 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";
|
||||
|
||||
type TSamlConfigServiceFactoryDep = {
|
||||
samlConfigDAL: Pick<TSamlConfigDALFactory, "create" | "findOne" | "update" | "findById">;
|
||||
userDAL: Pick<TUserDALFactory, "create" | "findOne" | "transaction" | "updateById" | "findById">;
|
||||
userAliasDAL: Pick<TUserAliasDALFactory, "create" | "findOne">;
|
||||
samlConfigDAL: TSamlConfigDALFactory;
|
||||
userDAL: Pick<TUserDALFactory, "create" | "findOne" | "transaction" | "updateById">;
|
||||
orgDAL: Pick<
|
||||
TOrgDALFactory,
|
||||
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
|
||||
>;
|
||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">;
|
||||
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "create" | "transaction">;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
tokenService: Pick<TAuthTokenServiceFactory, "createTokenForUser">;
|
||||
smtpService: Pick<TSmtpService, "sendMail">;
|
||||
};
|
||||
|
||||
export type TSamlConfigServiceFactory = ReturnType<typeof samlConfigServiceFactory>;
|
||||
@@ -61,13 +48,9 @@ export const samlConfigServiceFactory = ({
|
||||
samlConfigDAL,
|
||||
orgBotDAL,
|
||||
orgDAL,
|
||||
orgMembershipDAL,
|
||||
userDAL,
|
||||
userAliasDAL,
|
||||
permissionService,
|
||||
licenseService,
|
||||
tokenService,
|
||||
smtpService
|
||||
licenseService
|
||||
}: TSamlConfigServiceFactoryDep) => {
|
||||
const createSamlCfg = async ({
|
||||
cert,
|
||||
@@ -322,7 +305,7 @@ export const samlConfigServiceFactory = ({
|
||||
};
|
||||
|
||||
const samlLogin = async ({
|
||||
externalId,
|
||||
username,
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
@@ -331,40 +314,38 @@ export const samlConfigServiceFactory = ({
|
||||
relayState
|
||||
}: TSamlLoginDTO) => {
|
||||
const appCfg = getConfig();
|
||||
const serverCfg = await getServerCfg();
|
||||
const userAlias = await userAliasDAL.findOne({
|
||||
externalId,
|
||||
orgId,
|
||||
aliasType: UserAliasType.SAML
|
||||
});
|
||||
let user = await userDAL.findOne({ username });
|
||||
|
||||
const organization = await orgDAL.findOrgById(orgId);
|
||||
if (!organization) throw new BadRequestError({ message: "Org not found" });
|
||||
|
||||
let user: TUsers;
|
||||
if (userAlias) {
|
||||
user = await userDAL.transaction(async (tx) => {
|
||||
const foundUser = await userDAL.findById(userAlias.userId, tx);
|
||||
// TODO(dangtony98): remove this after aliases update
|
||||
if (authProvider === AuthMethod.KEYCLOAK_SAML && appCfg.LICENSE_SERVER_KEY) {
|
||||
throw new BadRequestError({ message: "Keycloak SAML is not yet available on Infisical Cloud" });
|
||||
}
|
||||
|
||||
if (user) {
|
||||
await userDAL.transaction(async (tx) => {
|
||||
const [orgMembership] = await orgDAL.findMembership(
|
||||
{
|
||||
[`${TableName.OrgMembership}.userId` as "userId"]: foundUser.id,
|
||||
userId: user.id,
|
||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||
},
|
||||
{ tx }
|
||||
);
|
||||
if (!orgMembership) {
|
||||
await orgMembershipDAL.create(
|
||||
await orgDAL.createMembership(
|
||||
{
|
||||
userId: userAlias.userId,
|
||||
inviteEmail: email,
|
||||
userId: user.id,
|
||||
orgId,
|
||||
inviteEmail: email,
|
||||
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
|
||||
);
|
||||
// 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(
|
||||
orgMembership.id,
|
||||
{
|
||||
@@ -373,97 +354,40 @@ export const samlConfigServiceFactory = ({
|
||||
tx
|
||||
);
|
||||
}
|
||||
|
||||
return foundUser;
|
||||
});
|
||||
} else {
|
||||
user = await userDAL.transaction(async (tx) => {
|
||||
let newUser: TUsers | undefined;
|
||||
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(
|
||||
const newUser = await userDAL.create(
|
||||
{
|
||||
userId: newUser.id,
|
||||
aliasType: UserAliasType.SAML,
|
||||
externalId,
|
||||
emails: email ? [email] : [],
|
||||
orgId
|
||||
username,
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
authMethods: [AuthMethod.EMAIL],
|
||||
isGhost: false
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const [orgMembership] = await orgDAL.findMembership(
|
||||
{
|
||||
[`${TableName.OrgMembership}.userId` as "userId"]: newUser.id,
|
||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||
},
|
||||
{ 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
|
||||
);
|
||||
}
|
||||
|
||||
await orgDAL.createMembership({
|
||||
inviteEmail: email,
|
||||
orgId,
|
||||
role: OrgMembershipRole.Member,
|
||||
status: OrgMembershipStatus.Invited
|
||||
});
|
||||
return newUser;
|
||||
});
|
||||
}
|
||||
|
||||
const isUserCompleted = Boolean(user.isAccepted);
|
||||
const providerAuthToken = jwt.sign(
|
||||
{
|
||||
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
||||
userId: user.id,
|
||||
username: user.username,
|
||||
...(user.email && { email: user.email, isEmailVerified: user.isEmailVerified }),
|
||||
firstName,
|
||||
lastName,
|
||||
organizationName: organization.name,
|
||||
organizationId: organization.id,
|
||||
organizationSlug: organization.slug,
|
||||
authMethod: authProvider,
|
||||
authType: UserAliasType.SAML,
|
||||
isUserCompleted,
|
||||
...(relayState
|
||||
? {
|
||||
@@ -479,22 +403,6 @@ export const samlConfigServiceFactory = ({
|
||||
|
||||
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 };
|
||||
};
|
||||
|
||||
|
@@ -45,8 +45,8 @@ export type TGetSamlCfgDTO =
|
||||
};
|
||||
|
||||
export type TSamlLoginDTO = {
|
||||
externalId: string;
|
||||
email: string;
|
||||
username: string;
|
||||
email?: string;
|
||||
firstName: string;
|
||||
lastName?: string;
|
||||
authProvider: string;
|
||||
|
@@ -2,31 +2,31 @@ import { TListScimGroups, TListScimUsers, TScimGroup, TScimUser } from "./scim-t
|
||||
|
||||
export const buildScimUserList = ({
|
||||
scimUsers,
|
||||
startIndex,
|
||||
offset,
|
||||
limit
|
||||
}: {
|
||||
scimUsers: TScimUser[];
|
||||
startIndex: number;
|
||||
offset: number;
|
||||
limit: number;
|
||||
}): TListScimUsers => {
|
||||
return {
|
||||
Resources: scimUsers,
|
||||
itemsPerPage: limit,
|
||||
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
|
||||
startIndex,
|
||||
startIndex: offset,
|
||||
totalResults: scimUsers.length
|
||||
};
|
||||
};
|
||||
|
||||
export const buildScimUser = ({
|
||||
orgMembershipId,
|
||||
userId,
|
||||
username,
|
||||
email,
|
||||
firstName,
|
||||
lastName,
|
||||
active
|
||||
}: {
|
||||
orgMembershipId: string;
|
||||
userId: string;
|
||||
username: string;
|
||||
email?: string | null;
|
||||
firstName: string;
|
||||
@@ -35,7 +35,7 @@ export const buildScimUser = ({
|
||||
}): TScimUser => {
|
||||
const scimUser = {
|
||||
schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
||||
id: orgMembershipId,
|
||||
id: userId,
|
||||
userName: username,
|
||||
displayName: `${firstName} ${lastName}`,
|
||||
name: {
|
||||
@@ -65,18 +65,18 @@ export const buildScimUser = ({
|
||||
|
||||
export const buildScimGroupList = ({
|
||||
scimGroups,
|
||||
startIndex,
|
||||
offset,
|
||||
limit
|
||||
}: {
|
||||
scimGroups: TScimGroup[];
|
||||
startIndex: number;
|
||||
offset: number;
|
||||
limit: number;
|
||||
}): TListScimGroups => {
|
||||
return {
|
||||
Resources: scimGroups,
|
||||
itemsPerPage: limit,
|
||||
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
|
||||
startIndex,
|
||||
startIndex: offset,
|
||||
totalResults: scimGroups.length
|
||||
};
|
||||
};
|
||||
|
@@ -2,7 +2,7 @@ import { ForbiddenError } from "@casl/ability";
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
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 { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
|
||||
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 { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
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 { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||
import { deleteOrgMembershipFn } from "@app/services/org/org-fns";
|
||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
||||
import { deleteOrgMembership } from "@app/services/org/org-fns";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-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 { 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 { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||
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 {
|
||||
TCreateScimGroupDTO,
|
||||
@@ -52,32 +50,27 @@ import {
|
||||
|
||||
type TScimServiceFactoryDep = {
|
||||
scimDAL: Pick<TScimDALFactory, "create" | "find" | "findById" | "deleteById">;
|
||||
userDAL: Pick<
|
||||
TUserDALFactory,
|
||||
"find" | "findOne" | "create" | "transaction" | "findUserEncKeyByUserIdsBatch" | "findById"
|
||||
>;
|
||||
userAliasDAL: Pick<TUserAliasDALFactory, "findOne" | "create" | "delete">;
|
||||
userDAL: Pick<TUserDALFactory, "find" | "findOne" | "create" | "transaction" | "findUserEncKeyByUserIdsBatch">;
|
||||
orgDAL: Pick<
|
||||
TOrgDALFactory,
|
||||
"createMembership" | "findById" | "findMembership" | "deleteMembershipById" | "transaction" | "updateMembershipById"
|
||||
"createMembership" | "findById" | "findMembership" | "deleteMembershipById" | "transaction"
|
||||
>;
|
||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "find" | "findOne" | "create" | "updateById">;
|
||||
projectDAL: Pick<TProjectDALFactory, "find" | "findProjectGhostUser">;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find" | "delete" | "findProjectMembershipsByUserId">;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find" | "delete">;
|
||||
groupDAL: Pick<
|
||||
TGroupDALFactory,
|
||||
"create" | "findOne" | "findAllGroupMembers" | "update" | "delete" | "findGroups" | "transaction"
|
||||
>;
|
||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
||||
userGroupMembershipDAL: Pick<
|
||||
TUserGroupMembershipDALFactory,
|
||||
"find" | "transaction" | "insertMany" | "filterProjectsByUserMembership" | "delete"
|
||||
>;
|
||||
userGroupMembershipDAL: TUserGroupMembershipDALFactory; // TODO: Pick
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany" | "delete">;
|
||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
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>;
|
||||
@@ -86,9 +79,7 @@ export const scimServiceFactory = ({
|
||||
licenseService,
|
||||
scimDAL,
|
||||
userDAL,
|
||||
userAliasDAL,
|
||||
orgDAL,
|
||||
orgMembershipDAL,
|
||||
projectDAL,
|
||||
projectMembershipDAL,
|
||||
groupDAL,
|
||||
@@ -96,6 +87,9 @@ export const scimServiceFactory = ({
|
||||
userGroupMembershipDAL,
|
||||
projectKeyDAL,
|
||||
projectBotDAL,
|
||||
accessApprovalRequestDAL,
|
||||
secretApprovalRequestDAL,
|
||||
secretApprovalPolicyDAL,
|
||||
permissionService,
|
||||
smtpService
|
||||
}: TScimServiceFactoryDep) => {
|
||||
@@ -175,7 +169,7 @@ export const scimServiceFactory = ({
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
if (!org.scimEnabled)
|
||||
@@ -193,11 +187,11 @@ export const scimServiceFactory = ({
|
||||
attributeName = "email";
|
||||
}
|
||||
|
||||
return { [attributeName]: parsedValue.replace(/"/g, "") };
|
||||
return { [attributeName]: parsedValue };
|
||||
};
|
||||
|
||||
const findOpts = {
|
||||
...(startIndex && { offset: startIndex - 1 }),
|
||||
...(offset && { offset }),
|
||||
...(limit && { limit })
|
||||
};
|
||||
|
||||
@@ -209,10 +203,10 @@ export const scimServiceFactory = ({
|
||||
findOpts
|
||||
);
|
||||
|
||||
const scimUsers = users.map(({ id, externalId, username, firstName, lastName, email }) =>
|
||||
const scimUsers = users.map(({ userId, username, firstName, lastName, email }) =>
|
||||
buildScimUser({
|
||||
orgMembershipId: id ?? "",
|
||||
username: externalId ?? username,
|
||||
userId: userId ?? "",
|
||||
username,
|
||||
firstName: firstName ?? "",
|
||||
lastName: lastName ?? "",
|
||||
email,
|
||||
@@ -222,16 +216,16 @@ export const scimServiceFactory = ({
|
||||
|
||||
return buildScimUserList({
|
||||
scimUsers,
|
||||
startIndex,
|
||||
offset,
|
||||
limit
|
||||
});
|
||||
};
|
||||
|
||||
const getScimUser = async ({ orgMembershipId, orgId }: TGetScimUserDTO) => {
|
||||
const getScimUser = async ({ userId, orgId }: TGetScimUserDTO) => {
|
||||
const [membership] = await orgDAL
|
||||
.findMembership({
|
||||
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
|
||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
|
||||
userId,
|
||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||
})
|
||||
.catch(() => {
|
||||
throw new ScimRequestError({
|
||||
@@ -253,8 +247,8 @@ export const scimServiceFactory = ({
|
||||
});
|
||||
|
||||
return buildScimUser({
|
||||
orgMembershipId: membership.id,
|
||||
username: membership.externalId ?? membership.username,
|
||||
userId: membership.userId as string,
|
||||
username: membership.username,
|
||||
email: membership.email ?? "",
|
||||
firstName: membership.firstName as string,
|
||||
lastName: membership.lastName as string,
|
||||
@@ -262,9 +256,7 @@ export const scimServiceFactory = ({
|
||||
});
|
||||
};
|
||||
|
||||
const createScimUser = async ({ externalId, email, firstName, lastName, orgId }: TCreateScimUserDTO) => {
|
||||
if (!email) throw new ScimRequestError({ detail: "Invalid request. Missing email.", status: 400 });
|
||||
|
||||
const createScimUser = async ({ username, email, firstName, lastName, orgId }: TCreateScimUserDTO) => {
|
||||
const org = await orgDAL.findById(orgId);
|
||||
|
||||
if (!org)
|
||||
@@ -279,121 +271,67 @@ export const scimServiceFactory = ({
|
||||
status: 403
|
||||
});
|
||||
|
||||
const appCfg = getConfig();
|
||||
const serverCfg = await getServerCfg();
|
||||
|
||||
const userAlias = await userAliasDAL.findOne({
|
||||
externalId,
|
||||
orgId,
|
||||
aliasType: UserAliasType.SAML
|
||||
let user = await userDAL.findOne({
|
||||
username
|
||||
});
|
||||
|
||||
const { user: createdUser, orgMembership: createdOrgMembership } = await userDAL.transaction(async (tx) => {
|
||||
let user: TUsers | undefined;
|
||||
let orgMembership: TOrgMemberships;
|
||||
if (userAlias) {
|
||||
user = await userDAL.findById(userAlias.userId, tx);
|
||||
orgMembership = await orgMembershipDAL.findOne(
|
||||
if (user) {
|
||||
await userDAL.transaction(async (tx) => {
|
||||
const [orgMembership] = await orgDAL.findMembership(
|
||||
{
|
||||
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
|
||||
},
|
||||
{ tx }
|
||||
);
|
||||
|
||||
orgMembership = foundOrgMembership;
|
||||
if (orgMembership)
|
||||
throw new ScimRequestError({
|
||||
detail: "User already exists in the database",
|
||||
status: 409
|
||||
});
|
||||
|
||||
if (!orgMembership) {
|
||||
orgMembership = await orgMembershipDAL.create(
|
||||
await orgDAL.createMembership(
|
||||
{
|
||||
userId: user.id,
|
||||
inviteEmail: email,
|
||||
orgId,
|
||||
inviteEmail: email,
|
||||
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
|
||||
);
|
||||
// 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
|
||||
status: OrgMembershipStatus.Invited
|
||||
},
|
||||
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) {
|
||||
await smtpService.sendMail({
|
||||
@@ -408,20 +346,20 @@ export const scimServiceFactory = ({
|
||||
}
|
||||
|
||||
return buildScimUser({
|
||||
orgMembershipId: createdOrgMembership.id,
|
||||
username: externalId,
|
||||
firstName: createdUser.firstName as string,
|
||||
lastName: createdUser.lastName as string,
|
||||
email: createdUser.email ?? "",
|
||||
userId: user.id,
|
||||
username: user.username,
|
||||
firstName: user.firstName as string,
|
||||
lastName: user.lastName as string,
|
||||
email: user.email ?? "",
|
||||
active: true
|
||||
});
|
||||
};
|
||||
|
||||
const updateScimUser = async ({ orgMembershipId, orgId, operations }: TUpdateScimUserDTO) => {
|
||||
const updateScimUser = async ({ userId, orgId, operations }: TUpdateScimUserDTO) => {
|
||||
const [membership] = await orgDAL
|
||||
.findMembership({
|
||||
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
|
||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
|
||||
userId,
|
||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||
})
|
||||
.catch(() => {
|
||||
throw new ScimRequestError({
|
||||
@@ -457,20 +395,18 @@ export const scimServiceFactory = ({
|
||||
});
|
||||
|
||||
if (!active) {
|
||||
await deleteOrgMembershipFn({
|
||||
await deleteOrgMembership({
|
||||
orgMembershipId: membership.id,
|
||||
orgId: membership.orgId,
|
||||
orgDAL,
|
||||
projectMembershipDAL,
|
||||
projectKeyDAL,
|
||||
userAliasDAL,
|
||||
licenseService
|
||||
projectDAL,
|
||||
projectMembershipDAL
|
||||
});
|
||||
}
|
||||
|
||||
return buildScimUser({
|
||||
orgMembershipId: membership.id,
|
||||
username: membership.externalId ?? membership.username,
|
||||
userId: membership.userId as string,
|
||||
username: membership.username,
|
||||
email: membership.email,
|
||||
firstName: membership.firstName 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
|
||||
.findMembership({
|
||||
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
|
||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
|
||||
userId,
|
||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||
})
|
||||
.catch(() => {
|
||||
throw new ScimRequestError({
|
||||
@@ -504,20 +440,19 @@ export const scimServiceFactory = ({
|
||||
});
|
||||
|
||||
if (!active) {
|
||||
await deleteOrgMembershipFn({
|
||||
// tx
|
||||
await deleteOrgMembership({
|
||||
orgMembershipId: membership.id,
|
||||
orgId: membership.orgId,
|
||||
orgDAL,
|
||||
projectMembershipDAL,
|
||||
projectKeyDAL,
|
||||
userAliasDAL,
|
||||
licenseService
|
||||
projectDAL,
|
||||
projectMembershipDAL
|
||||
});
|
||||
}
|
||||
|
||||
return buildScimUser({
|
||||
orgMembershipId: membership.id,
|
||||
username: membership.externalId ?? membership.username,
|
||||
userId: membership.userId as string,
|
||||
username: membership.username,
|
||||
email: membership.email,
|
||||
firstName: membership.firstName as string,
|
||||
lastName: membership.lastName as string,
|
||||
@@ -525,11 +460,18 @@ export const scimServiceFactory = ({
|
||||
});
|
||||
};
|
||||
|
||||
const deleteScimUser = async ({ orgMembershipId, orgId }: TDeleteScimUserDTO) => {
|
||||
const [membership] = await orgDAL.findMembership({
|
||||
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
|
||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
|
||||
});
|
||||
const deleteScimUser = async ({ userId, orgId }: TDeleteScimUserDTO) => {
|
||||
const [membership] = await orgDAL
|
||||
.findMembership({
|
||||
userId,
|
||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||
})
|
||||
.catch(() => {
|
||||
throw new ScimRequestError({
|
||||
detail: "User not found",
|
||||
status: 404
|
||||
});
|
||||
});
|
||||
|
||||
if (!membership)
|
||||
throw new ScimRequestError({
|
||||
@@ -544,20 +486,18 @@ export const scimServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
await deleteOrgMembershipFn({
|
||||
await deleteOrgMembership({
|
||||
orgMembershipId: membership.id,
|
||||
orgId: membership.orgId,
|
||||
orgDAL,
|
||||
projectMembershipDAL,
|
||||
projectKeyDAL,
|
||||
userAliasDAL,
|
||||
licenseService
|
||||
projectDAL,
|
||||
projectMembershipDAL
|
||||
});
|
||||
|
||||
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);
|
||||
if (!plan.groups)
|
||||
throw new BadRequestError({
|
||||
@@ -578,27 +518,21 @@ export const scimServiceFactory = ({
|
||||
status: 403
|
||||
});
|
||||
|
||||
const groups = await groupDAL.findGroups(
|
||||
{
|
||||
orgId
|
||||
},
|
||||
{
|
||||
offset: startIndex - 1,
|
||||
limit
|
||||
}
|
||||
);
|
||||
const groups = await groupDAL.findGroups({
|
||||
orgId
|
||||
});
|
||||
|
||||
const scimGroups = groups.map((group) =>
|
||||
buildScimGroup({
|
||||
groupId: group.id,
|
||||
name: group.name,
|
||||
members: [] // does this need to be populated?
|
||||
members: []
|
||||
})
|
||||
);
|
||||
|
||||
return buildScimGroupList({
|
||||
scimGroups,
|
||||
startIndex,
|
||||
offset,
|
||||
limit
|
||||
});
|
||||
};
|
||||
@@ -637,15 +571,9 @@ export const scimServiceFactory = ({
|
||||
);
|
||||
|
||||
if (members && members.length) {
|
||||
const orgMemberships = await orgMembershipDAL.find({
|
||||
$in: {
|
||||
id: members.map((member) => member.value)
|
||||
}
|
||||
});
|
||||
|
||||
const newMembers = await addUsersToGroupByUserIds({
|
||||
group,
|
||||
userIds: orgMemberships.map((membership) => membership.userId as string),
|
||||
userIds: members.map((member) => member.value),
|
||||
userDAL,
|
||||
userGroupMembershipDAL,
|
||||
orgDAL,
|
||||
@@ -662,19 +590,12 @@ export const scimServiceFactory = ({
|
||||
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({
|
||||
groupId: newGroup.group.id,
|
||||
name: newGroup.group.name,
|
||||
members: orgMemberships.map(({ id, firstName, lastName }) => ({
|
||||
value: id,
|
||||
display: `${firstName} ${lastName}`
|
||||
members: newGroup.newMembers.map((member) => ({
|
||||
value: member.id,
|
||||
display: `${member.firstName} ${member.lastName}`
|
||||
}))
|
||||
});
|
||||
};
|
||||
@@ -703,22 +624,15 @@ export const scimServiceFactory = ({
|
||||
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({
|
||||
groupId: group.id,
|
||||
name: group.name,
|
||||
members: orgMemberships.map(({ id, firstName, lastName }) => ({
|
||||
value: id,
|
||||
display: `${firstName} ${lastName}`
|
||||
}))
|
||||
members: users
|
||||
.filter((user) => user.isPartOfGroup)
|
||||
.map((user) => ({
|
||||
value: user.id,
|
||||
display: `${user.firstName} ${user.lastName}`
|
||||
}))
|
||||
});
|
||||
};
|
||||
|
||||
@@ -762,13 +676,7 @@ export const scimServiceFactory = ({
|
||||
}
|
||||
|
||||
if (members) {
|
||||
const orgMemberships = await orgMembershipDAL.find({
|
||||
$in: {
|
||||
id: members.map((member) => member.value)
|
||||
}
|
||||
});
|
||||
|
||||
const membersIdsSet = new Set(orgMemberships.map((orgMembership) => orgMembership.userId));
|
||||
const membersIdsSet = new Set(members.map((member) => member.value));
|
||||
|
||||
const directMemberUserIds = (
|
||||
await userGroupMembershipDAL.find({
|
||||
@@ -787,13 +695,13 @@ export const scimServiceFactory = ({
|
||||
const allMembersUserIds = directMemberUserIds.concat(pendingGroupAdditionsUserIds);
|
||||
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));
|
||||
|
||||
if (toAddUserIds.length) {
|
||||
await addUsersToGroupByUserIds({
|
||||
group,
|
||||
userIds: toAddUserIds.map((member) => member.userId as string),
|
||||
userIds: toAddUserIds.map((member) => member.value),
|
||||
userDAL,
|
||||
userGroupMembershipDAL,
|
||||
orgDAL,
|
||||
@@ -811,6 +719,9 @@ export const scimServiceFactory = ({
|
||||
userIds: toRemoveUserIds,
|
||||
userDAL,
|
||||
userGroupMembershipDAL,
|
||||
secretApprovalPolicyDAL,
|
||||
accessApprovalRequestDAL,
|
||||
secretApprovalRequestDAL,
|
||||
groupProjectDAL,
|
||||
projectKeyDAL,
|
||||
tx
|
||||
|
@@ -12,7 +12,7 @@ export type TDeleteScimTokenDTO = {
|
||||
// SCIM server endpoint types
|
||||
|
||||
export type TListScimUsersDTO = {
|
||||
startIndex: number;
|
||||
offset: number;
|
||||
limit: number;
|
||||
filter?: string;
|
||||
orgId: string;
|
||||
@@ -27,12 +27,12 @@ export type TListScimUsers = {
|
||||
};
|
||||
|
||||
export type TGetScimUserDTO = {
|
||||
orgMembershipId: string;
|
||||
userId: string;
|
||||
orgId: string;
|
||||
};
|
||||
|
||||
export type TCreateScimUserDTO = {
|
||||
externalId: string;
|
||||
username: string;
|
||||
email?: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
@@ -40,7 +40,7 @@ export type TCreateScimUserDTO = {
|
||||
};
|
||||
|
||||
export type TUpdateScimUserDTO = {
|
||||
orgMembershipId: string;
|
||||
userId: string;
|
||||
orgId: string;
|
||||
operations: {
|
||||
op: string;
|
||||
@@ -54,18 +54,18 @@ export type TUpdateScimUserDTO = {
|
||||
};
|
||||
|
||||
export type TReplaceScimUserDTO = {
|
||||
orgMembershipId: string;
|
||||
userId: string;
|
||||
active: boolean;
|
||||
orgId: string;
|
||||
};
|
||||
|
||||
export type TDeleteScimUserDTO = {
|
||||
orgMembershipId: string;
|
||||
userId: string;
|
||||
orgId: string;
|
||||
};
|
||||
|
||||
export type TListScimGroupsDTO = {
|
||||
startIndex: number;
|
||||
offset: number;
|
||||
limit: number;
|
||||
orgId: string;
|
||||
};
|
||||
|
@@ -20,7 +20,7 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
||||
`${TableName.SecretApprovalPolicy}.id`,
|
||||
`${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("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||
.select(tx.ref("id").withSchema(TableName.Environment).as("envId"))
|
||||
@@ -33,18 +33,18 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
||||
const doc = await sapFindQuery(tx || db, {
|
||||
[`${TableName.SecretApprovalPolicy}.id` as "id"]: id
|
||||
});
|
||||
const formatedDoc = mergeOneToManyRelation(
|
||||
const formattedDoc = mergeOneToManyRelation(
|
||||
doc,
|
||||
"id",
|
||||
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||
({ approverUserId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||
...el,
|
||||
envId,
|
||||
environment: { id: envId, name, slug }
|
||||
}),
|
||||
({ approverId }) => approverId,
|
||||
({ approverUserId }) => approverUserId,
|
||||
"approvers"
|
||||
);
|
||||
return formatedDoc?.[0];
|
||||
return formattedDoc?.[0];
|
||||
} catch (error) {
|
||||
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) => {
|
||||
try {
|
||||
const docs = await sapFindQuery(tx || db, filter);
|
||||
const formatedDoc = mergeOneToManyRelation(
|
||||
const formattedDoc = mergeOneToManyRelation(
|
||||
docs,
|
||||
"id",
|
||||
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||
({ approverUserId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||
...el,
|
||||
envId,
|
||||
environment: { id: envId, name, slug }
|
||||
}),
|
||||
({ approverId }) => approverId,
|
||||
({ approverUserId }) => approverUserId,
|
||||
"approvers"
|
||||
);
|
||||
return formatedDoc;
|
||||
return formattedDoc;
|
||||
} catch (error) {
|
||||
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 { 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 { TSecretApprovalPolicyApproverDALFactory } from "./secret-approval-policy-approver-dal";
|
||||
import { TSecretApprovalPolicyDALFactory } from "./secret-approval-policy-dal";
|
||||
@@ -29,6 +30,7 @@ type TSecretApprovalPolicyServiceFactoryDep = {
|
||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
||||
secretApprovalPolicyApproverDAL: TSecretApprovalPolicyApproverDALFactory;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
|
||||
userDAL: Pick<TUserDALFactory, "findUsersByProjectId" | "findUserByProjectId">;
|
||||
};
|
||||
|
||||
export type TSecretApprovalPolicyServiceFactory = ReturnType<typeof secretApprovalPolicyServiceFactory>;
|
||||
@@ -38,7 +40,7 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
permissionService,
|
||||
secretApprovalPolicyApproverDAL,
|
||||
projectEnvDAL,
|
||||
projectMembershipDAL
|
||||
userDAL
|
||||
}: TSecretApprovalPolicyServiceFactoryDep) => {
|
||||
const createSecretApprovalPolicy = async ({
|
||||
name,
|
||||
@@ -69,11 +71,12 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
const env = await projectEnvDAL.findOne({ slug: environment, projectId });
|
||||
if (!env) throw new BadRequestError({ message: "Environment not found" });
|
||||
|
||||
const secretApprovers = await projectMembershipDAL.find({
|
||||
const secretApproverUsers = await userDAL.findUsersByProjectId(
|
||||
projectId,
|
||||
$in: { id: approvers }
|
||||
});
|
||||
if (secretApprovers.length !== approvers.length)
|
||||
approvers.map((approverUserId) => approverUserId)
|
||||
);
|
||||
|
||||
if (secretApproverUsers.length !== approvers.length)
|
||||
throw new BadRequestError({ message: "Approver not found in project" });
|
||||
|
||||
const secretApproval = await secretApprovalPolicyDAL.transaction(async (tx) => {
|
||||
@@ -87,8 +90,8 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
tx
|
||||
);
|
||||
await secretApprovalPolicyApproverDAL.insertMany(
|
||||
secretApprovers.map(({ id }) => ({
|
||||
approverId: id,
|
||||
secretApproverUsers.map(({ id }) => ({
|
||||
approverUserId: id,
|
||||
policyId: doc.id
|
||||
})),
|
||||
tx
|
||||
@@ -132,21 +135,19 @@ export const secretApprovalPolicyServiceFactory = ({
|
||||
tx
|
||||
);
|
||||
if (approvers) {
|
||||
const secretApprovers = await projectMembershipDAL.find(
|
||||
{
|
||||
projectId: secretApprovalPolicy.projectId,
|
||||
$in: { id: approvers }
|
||||
},
|
||||
{ tx }
|
||||
const secretApproverUsers = await userDAL.findUsersByProjectId(
|
||||
secretApprovalPolicy.projectId,
|
||||
approvers.map((approverUserId) => approverUserId)
|
||||
);
|
||||
if (secretApprovers.length !== approvers.length)
|
||||
|
||||
if (secretApproverUsers.length !== approvers.length)
|
||||
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" });
|
||||
await secretApprovalPolicyApproverDAL.delete({ policyId: doc.id }, tx);
|
||||
await secretApprovalPolicyApproverDAL.insertMany(
|
||||
secretApprovers.map(({ id }) => ({
|
||||
approverId: id,
|
||||
secretApproverUsers.map((user) => ({
|
||||
approverUserId: user.id,
|
||||
policyId: doc.id
|
||||
})),
|
||||
tx
|
||||
|
@@ -16,7 +16,7 @@ export type TSecretApprovalRequestDALFactory = ReturnType<typeof secretApprovalR
|
||||
|
||||
type TFindQueryFilter = {
|
||||
projectId: string;
|
||||
membershipId: string;
|
||||
actorId: string;
|
||||
status?: RequestState;
|
||||
environment?: string;
|
||||
committer?: string;
|
||||
@@ -49,7 +49,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
)
|
||||
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
||||
.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("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"),
|
||||
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("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
||||
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) => {
|
||||
try {
|
||||
const sql = findQuery({ [`${TableName.SecretApprovalRequest}.id` as "id"]: id }, tx || db);
|
||||
const docs = await sql;
|
||||
const formatedDoc = sqlNestRelationships({
|
||||
const formattedDoc = sqlNestRelationships({
|
||||
data: docs,
|
||||
key: "id",
|
||||
parentMapper: (el) => ({
|
||||
@@ -84,20 +84,20 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
label: "reviewers" as const,
|
||||
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 {
|
||||
...formatedDoc[0],
|
||||
policy: { ...formatedDoc[0].policy, approvers: formatedDoc[0].approvers }
|
||||
...formattedDoc[0],
|
||||
policy: { ...formattedDoc[0].policy, approvers: formattedDoc[0].approvers }
|
||||
};
|
||||
} catch (error) {
|
||||
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 {
|
||||
const docs = await (tx || db)
|
||||
.with(
|
||||
@@ -110,12 +110,12 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
`${TableName.SecretApprovalRequest}.policyId`,
|
||||
`${TableName.SecretApprovalPolicyApprover}.policyId`
|
||||
)
|
||||
.where({ projectId })
|
||||
.where({ [`${TableName.Environment}.projectId` as "projectId"]: projectId })
|
||||
.andWhere(
|
||||
(bd) =>
|
||||
void bd
|
||||
.where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId)
|
||||
.orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId)
|
||||
.where(`${TableName.SecretApprovalPolicyApprover}.approverUserId`, approverUserId)
|
||||
.orWhere(`${TableName.SecretApprovalRequest}.committerUserId`, approverUserId)
|
||||
)
|
||||
.select("status", `${TableName.SecretApprovalRequest}.id`)
|
||||
.groupBy(`${TableName.SecretApprovalRequest}.id`, "status")
|
||||
@@ -142,7 +142,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
};
|
||||
|
||||
const findByProjectId = async (
|
||||
{ status, limit = 20, offset = 0, projectId, committer, environment, membershipId }: TFindQueryFilter,
|
||||
{ status, limit = 20, offset = 0, projectId, committer, environment, actorId }: TFindQueryFilter,
|
||||
tx?: Knex
|
||||
) => {
|
||||
try {
|
||||
@@ -173,21 +173,21 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
)
|
||||
.where(
|
||||
stripUndefinedInWhere({
|
||||
projectId,
|
||||
[`${TableName.Environment}.projectId`]: projectId,
|
||||
[`${TableName.Environment}.slug` as "slug"]: environment,
|
||||
[`${TableName.SecretApprovalRequest}.status`]: status,
|
||||
committerId: committer
|
||||
committerUserId: committer
|
||||
})
|
||||
)
|
||||
.andWhere(
|
||||
(bd) =>
|
||||
void bd
|
||||
.where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId)
|
||||
.orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId)
|
||||
.where(`${TableName.SecretApprovalPolicyApprover}.approverUserId`, actorId)
|
||||
.orWhere(`${TableName.SecretApprovalRequest}.committerUserId`, actorId)
|
||||
)
|
||||
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
||||
.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("id").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerMemberId"),
|
||||
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("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
|
||||
db.ref("approverId").withSchema(TableName.SecretApprovalPolicyApprover)
|
||||
db.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover)
|
||||
)
|
||||
.orderBy("createdAt", "desc");
|
||||
|
||||
@@ -217,7 +217,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
parentMapper: (el) => ({
|
||||
...SecretApprovalRequestsSchema.parse(el),
|
||||
environment: el.environment,
|
||||
projectId: el.projectId,
|
||||
projectId: el.envProjectId,
|
||||
policy: {
|
||||
id: el.policyId,
|
||||
name: el.policyName,
|
||||
@@ -232,9 +232,9 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
||||
mapper: ({ reviewerMemberId: member, reviewerStatus: s }) => (member ? { member, status: s } : undefined)
|
||||
},
|
||||
{
|
||||
key: "approverId",
|
||||
key: "approverUserId",
|
||||
label: "approvers" as const,
|
||||
mapper: ({ approverId }) => approverId
|
||||
mapper: ({ approverUserId }) => approverUserId
|
||||
},
|
||||
{
|
||||
key: "commitId",
|
||||
|
@@ -113,7 +113,7 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
|
||||
db.ref("secretCommentTag").withSchema(TableName.SecretVersion).as("secVerCommentTag"),
|
||||
db.ref("secretCommentCiphertext").withSchema(TableName.SecretVersion).as("secVerCommentCiphertext")
|
||||
);
|
||||
const formatedDoc = sqlNestRelationships({
|
||||
const formattedDoc = sqlNestRelationships({
|
||||
data: doc,
|
||||
key: "id",
|
||||
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,
|
||||
secret: secret?.[0],
|
||||
secretVersion: secretVersion?.[0]
|
||||
|
@@ -7,24 +7,14 @@ import {
|
||||
SecretType,
|
||||
TSecretApprovalRequestsSecretsInsert
|
||||
} from "@app/db/schemas";
|
||||
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
import { groupBy, pick, unique } from "@app/lib/fn";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
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 {
|
||||
fnSecretBlindIndexCheck,
|
||||
fnSecretBlindIndexCheckV2,
|
||||
fnSecretBulkDelete,
|
||||
fnSecretBulkInsert,
|
||||
fnSecretBulkUpdate,
|
||||
getAllNestedSecretReferences
|
||||
} from "@app/services/secret/secret-fns";
|
||||
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 { TSecretVersionTagDALFactory } from "@app/services/secret/secret-version-tag-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 {
|
||||
ApprovalStatus,
|
||||
CommitType,
|
||||
RequestState,
|
||||
TApprovalRequestCountDTO,
|
||||
TGenerateSecretApprovalRequestDTO,
|
||||
@@ -51,11 +42,10 @@ import {
|
||||
|
||||
type TSecretApprovalRequestServiceFactoryDep = {
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
|
||||
secretApprovalRequestDAL: TSecretApprovalRequestDALFactory;
|
||||
secretApprovalRequestSecretDAL: TSecretApprovalRequestSecretDALFactory;
|
||||
secretApprovalRequestReviewerDAL: TSecretApprovalRequestReviewerDALFactory;
|
||||
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath" | "findSecretPathByFolderIds">;
|
||||
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath" | "findById" | "findSecretPathByFolderIds">;
|
||||
secretDAL: TSecretDALFactory;
|
||||
secretTagDAL: Pick<TSecretTagDALFactory, "findManyTagsById" | "saveTagsToSecret" | "deleteTagsManySecret">;
|
||||
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
|
||||
@@ -63,7 +53,15 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
||||
secretVersionDAL: Pick<TSecretVersionDALFactory, "findLatestVersionMany" | "insertMany">;
|
||||
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
|
||||
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>;
|
||||
@@ -80,14 +78,14 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
projectDAL,
|
||||
permissionService,
|
||||
snapshotService,
|
||||
secretService,
|
||||
secretVersionDAL,
|
||||
secretQueueService,
|
||||
projectBotService
|
||||
secretQueueService
|
||||
}: TSecretApprovalRequestServiceFactoryDep) => {
|
||||
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
|
||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||
|
||||
const { membership } = await permissionService.getProjectPermission(
|
||||
await permissionService.getProjectPermission(
|
||||
actor as ActorType.USER,
|
||||
actorId,
|
||||
projectId,
|
||||
@@ -95,7 +93,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
actorOrgId
|
||||
);
|
||||
|
||||
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, membership.id);
|
||||
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, actorId);
|
||||
return count;
|
||||
};
|
||||
|
||||
@@ -113,19 +111,14 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
}: TListApprovalsDTO) => {
|
||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||
|
||||
const { membership } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
);
|
||||
await permissionService.getProjectPermission(actor, actorId, projectId, actorAuthMethod, actorOrgId);
|
||||
|
||||
const approvals = await secretApprovalRequestDAL.findByProjectId({
|
||||
projectId,
|
||||
committer,
|
||||
environment,
|
||||
status,
|
||||
membershipId: membership.id,
|
||||
actorId,
|
||||
limit,
|
||||
offset
|
||||
});
|
||||
@@ -145,7 +138,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
|
||||
|
||||
const { policy } = secretApprovalRequest;
|
||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
||||
const { hasRole } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
secretApprovalRequest.projectId,
|
||||
@@ -154,8 +147,8 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
);
|
||||
if (
|
||||
!hasRole(ProjectMembershipRole.Admin) &&
|
||||
secretApprovalRequest.committerId !== membership.id &&
|
||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
||||
secretApprovalRequest.committerUserId !== actorId &&
|
||||
!policy.approvers.find((approverUserId) => approverUserId === actorId)
|
||||
) {
|
||||
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" });
|
||||
|
||||
const { policy } = secretApprovalRequest;
|
||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
||||
const { hasRole } = await permissionService.getProjectPermission(
|
||||
ActorType.USER,
|
||||
actorId,
|
||||
secretApprovalRequest.projectId,
|
||||
@@ -189,8 +182,8 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
);
|
||||
if (
|
||||
!hasRole(ProjectMembershipRole.Admin) &&
|
||||
secretApprovalRequest.committerId !== membership.id &&
|
||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
||||
secretApprovalRequest.committerUserId !== actorId &&
|
||||
!policy.approvers.find((approverUserId) => approverUserId === actorId)
|
||||
) {
|
||||
throw new UnauthorizedError({ message: "User has no access" });
|
||||
}
|
||||
@@ -198,7 +191,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
const review = await secretApprovalRequestReviewerDAL.findOne(
|
||||
{
|
||||
requestId: secretApprovalRequest.id,
|
||||
member: membership.id
|
||||
memberUserId: actorId
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -207,7 +200,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
{
|
||||
status,
|
||||
requestId: secretApprovalRequest.id,
|
||||
member: membership.id
|
||||
memberUserId: actorId
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -230,7 +223,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
||||
|
||||
const { policy } = secretApprovalRequest;
|
||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
||||
const { hasRole } = await permissionService.getProjectPermission(
|
||||
ActorType.USER,
|
||||
actorId,
|
||||
secretApprovalRequest.projectId,
|
||||
@@ -239,8 +232,8 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
);
|
||||
if (
|
||||
!hasRole(ProjectMembershipRole.Admin) &&
|
||||
secretApprovalRequest.committerId !== membership.id &&
|
||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
||||
secretApprovalRequest.committerUserId !== actorId &&
|
||||
!policy.approvers.find((approverUserId) => approverUserId === actorId)
|
||||
) {
|
||||
throw new UnauthorizedError({ message: "User has no access" });
|
||||
}
|
||||
@@ -253,8 +246,9 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
|
||||
const updatedRequest = await secretApprovalRequestDAL.updateById(secretApprovalRequest.id, {
|
||||
status,
|
||||
statusChangeBy: membership.id
|
||||
statusChangeByUserId: actorId
|
||||
});
|
||||
|
||||
return { ...secretApprovalRequest, ...updatedRequest };
|
||||
};
|
||||
|
||||
@@ -270,7 +264,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
|
||||
|
||||
const { policy, folderId, projectId } = secretApprovalRequest;
|
||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
||||
const { hasRole } = await permissionService.getProjectPermission(
|
||||
ActorType.USER,
|
||||
actorId,
|
||||
projectId,
|
||||
@@ -280,8 +274,8 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
|
||||
if (
|
||||
!hasRole(ProjectMembershipRole.Admin) &&
|
||||
secretApprovalRequest.committerId !== membership.id &&
|
||||
!policy.approvers.find((approverId) => approverId === membership.id)
|
||||
secretApprovalRequest.committerUserId !== actorId &&
|
||||
!policy.approvers.find((approverUserId) => approverUserId === actorId)
|
||||
) {
|
||||
throw new UnauthorizedError({ message: "User has no access" });
|
||||
}
|
||||
@@ -292,19 +286,18 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
const hasMinApproval =
|
||||
secretApprovalRequest.policy.approvals <=
|
||||
secretApprovalRequest.policy.approvers.filter(
|
||||
(approverId) => reviewers[approverId.toString()] === ApprovalStatus.APPROVED
|
||||
(approverUserId) => reviewers[approverUserId.toString()] === ApprovalStatus.APPROVED
|
||||
).length;
|
||||
|
||||
if (!hasMinApproval) throw new BadRequestError({ message: "Doesn't have minimum approvals needed" });
|
||||
const secretApprovalSecrets = await secretApprovalRequestSecretDAL.findByRequestId(secretApprovalRequest.id);
|
||||
if (!secretApprovalSecrets) throw new BadRequestError({ message: "No secrets found" });
|
||||
|
||||
const conflicts: Array<{ secretId: string; op: SecretOperations }> = [];
|
||||
let secretCreationCommits = secretApprovalSecrets.filter(({ op }) => op === SecretOperations.Create);
|
||||
const conflicts: Array<{ secretId: string; op: CommitType }> = [];
|
||||
let secretCreationCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Create);
|
||||
if (secretCreationCommits.length) {
|
||||
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await fnSecretBlindIndexCheckV2({
|
||||
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await secretService.fnSecretBlindIndexCheckV2({
|
||||
folderId,
|
||||
secretDAL,
|
||||
inputSecrets: secretCreationCommits.map(({ secretBlindIndex }) => {
|
||||
if (!secretBlindIndex) {
|
||||
throw new BadRequestError({
|
||||
@@ -317,19 +310,17 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
secretCreationCommits
|
||||
.filter(({ secretBlindIndex }) => conflictGroupByBlindIndex[secretBlindIndex || ""])
|
||||
.forEach((el) => {
|
||||
conflicts.push({ op: SecretOperations.Create, secretId: el.id });
|
||||
conflicts.push({ op: CommitType.Create, secretId: el.id });
|
||||
});
|
||||
secretCreationCommits = secretCreationCommits.filter(
|
||||
({ secretBlindIndex }) => !conflictGroupByBlindIndex[secretBlindIndex || ""]
|
||||
);
|
||||
}
|
||||
|
||||
let secretUpdationCommits = secretApprovalSecrets.filter(({ op }) => op === SecretOperations.Update);
|
||||
let secretUpdationCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Update);
|
||||
if (secretUpdationCommits.length) {
|
||||
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await fnSecretBlindIndexCheckV2({
|
||||
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await secretService.fnSecretBlindIndexCheckV2({
|
||||
folderId,
|
||||
secretDAL,
|
||||
userId: "",
|
||||
inputSecrets: secretUpdationCommits
|
||||
.filter(({ secretBlindIndex, secret }) => secret && secret.secretBlindIndex !== secretBlindIndex)
|
||||
.map(({ secretBlindIndex }) => {
|
||||
@@ -347,7 +338,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
(secretBlindIndex && conflictGroupByBlindIndex[secretBlindIndex]) || !secretId
|
||||
)
|
||||
.forEach((el) => {
|
||||
conflicts.push({ op: SecretOperations.Update, secretId: el.id });
|
||||
conflicts.push({ op: CommitType.Update, secretId: el.id });
|
||||
});
|
||||
|
||||
secretUpdationCommits = secretUpdationCommits.filter(
|
||||
@@ -356,11 +347,11 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
);
|
||||
}
|
||||
|
||||
const secretDeletionCommits = secretApprovalSecrets.filter(({ op }) => op === SecretOperations.Delete);
|
||||
const botKey = await projectBotService.getBotKey(projectId).catch(() => null);
|
||||
const secretDeletionCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Delete);
|
||||
|
||||
const mergeStatus = await secretApprovalRequestDAL.transaction(async (tx) => {
|
||||
const newSecrets = secretCreationCommits.length
|
||||
? await fnSecretBulkInsert({
|
||||
? await secretService.fnSecretBulkInsert({
|
||||
tx,
|
||||
folderId,
|
||||
inputSecrets: secretCreationCommits.map((el) => ({
|
||||
@@ -384,17 +375,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
]),
|
||||
tags: el?.tags.map(({ id }) => id),
|
||||
version: 1,
|
||||
type: SecretType.Shared,
|
||||
references: botKey
|
||||
? getAllNestedSecretReferences(
|
||||
decryptSymmetric128BitHexKeyUTF8({
|
||||
ciphertext: el.secretValueCiphertext,
|
||||
iv: el.secretValueIV,
|
||||
tag: el.secretValueTag,
|
||||
key: botKey
|
||||
})
|
||||
)
|
||||
: undefined
|
||||
type: SecretType.Shared
|
||||
})),
|
||||
secretDAL,
|
||||
secretVersionDAL,
|
||||
@@ -403,7 +384,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
})
|
||||
: [];
|
||||
const updatedSecrets = secretUpdationCommits.length
|
||||
? await fnSecretBulkUpdate({
|
||||
? await secretService.fnSecretBulkUpdate({
|
||||
folderId,
|
||||
projectId,
|
||||
tx,
|
||||
@@ -429,17 +410,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
"secretReminderNote",
|
||||
"secretReminderRepeatDays",
|
||||
"secretBlindIndex"
|
||||
]),
|
||||
references: botKey
|
||||
? getAllNestedSecretReferences(
|
||||
decryptSymmetric128BitHexKeyUTF8({
|
||||
ciphertext: el.secretValueCiphertext,
|
||||
iv: el.secretValueIV,
|
||||
tag: el.secretValueTag,
|
||||
key: botKey
|
||||
})
|
||||
)
|
||||
: undefined
|
||||
])
|
||||
}
|
||||
})),
|
||||
secretDAL,
|
||||
@@ -449,13 +420,11 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
})
|
||||
: [];
|
||||
const deletedSecret = secretDeletionCommits.length
|
||||
? await fnSecretBulkDelete({
|
||||
? await secretService.fnSecretBulkDelete({
|
||||
projectId,
|
||||
folderId,
|
||||
tx,
|
||||
actorId: "",
|
||||
secretDAL,
|
||||
secretQueueService,
|
||||
inputSecrets: secretDeletionCommits.map(({ secretBlindIndex }) => {
|
||||
if (!secretBlindIndex) {
|
||||
throw new BadRequestError({
|
||||
@@ -472,7 +441,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
conflicts: JSON.stringify(conflicts),
|
||||
hasMerged: true,
|
||||
status: RequestState.Closed,
|
||||
statusChangeBy: membership.id
|
||||
statusChangeByUserId: actorId
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -482,14 +451,12 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
};
|
||||
});
|
||||
await snapshotService.performSnapshot(folderId);
|
||||
const [folder] = await folderDAL.findSecretPathByFolderIds(projectId, [folderId]);
|
||||
if (!folder) throw new BadRequestError({ message: "Folder not found" });
|
||||
const folder = await folderDAL.findById(folderId);
|
||||
// TODO(akhilmhdh-pg): change query to do secret path from folder
|
||||
await secretQueueService.syncSecrets({
|
||||
projectId,
|
||||
secretPath: folder.path,
|
||||
environmentSlug: folder.environmentSlug,
|
||||
actorId,
|
||||
actor
|
||||
secretPath: "/",
|
||||
environment: folder?.environment.envSlug as string
|
||||
});
|
||||
return mergeStatus;
|
||||
};
|
||||
@@ -509,7 +476,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
}: TGenerateSecretApprovalRequestDTO) => {
|
||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||
|
||||
const { permission, membership } = await permissionService.getProjectPermission(
|
||||
const { permission } = await permissionService.getProjectPermission(
|
||||
actor,
|
||||
actorId,
|
||||
projectId,
|
||||
@@ -537,9 +504,9 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
const commits: Omit<TSecretApprovalRequestsSecretsInsert, "requestId">[] = [];
|
||||
const commitTagIds: Record<string, string[]> = {};
|
||||
// for created secret approval change
|
||||
const createdSecrets = data[SecretOperations.Create];
|
||||
const createdSecrets = data[CommitType.Create];
|
||||
if (createdSecrets && createdSecrets?.length) {
|
||||
const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({
|
||||
const { keyName2BlindIndex } = await secretService.fnSecretBlindIndexCheck({
|
||||
inputSecrets: createdSecrets,
|
||||
folderId,
|
||||
isNew: true,
|
||||
@@ -550,7 +517,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
commits.push(
|
||||
...createdSecrets.map(({ secretName, ...el }) => ({
|
||||
...el,
|
||||
op: SecretOperations.Create as const,
|
||||
op: CommitType.Create as const,
|
||||
version: 1,
|
||||
secretBlindIndex: keyName2BlindIndex[secretName],
|
||||
algorithm: SecretEncryptionAlgo.AES_256_GCM,
|
||||
@@ -562,12 +529,12 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
});
|
||||
}
|
||||
// not secret approval for update operations
|
||||
const updatedSecrets = data[SecretOperations.Update];
|
||||
const updatedSecrets = data[CommitType.Update];
|
||||
if (updatedSecrets && updatedSecrets?.length) {
|
||||
// get all blind index
|
||||
// Find all those secrets
|
||||
// if not throw not found
|
||||
const { keyName2BlindIndex, secrets: secretsToBeUpdated } = await fnSecretBlindIndexCheck({
|
||||
const { keyName2BlindIndex, secrets: secretsToBeUpdated } = await secretService.fnSecretBlindIndexCheck({
|
||||
inputSecrets: updatedSecrets,
|
||||
folderId,
|
||||
isNew: false,
|
||||
@@ -578,8 +545,8 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
// now find any secret that needs to update its name
|
||||
// same process as above
|
||||
const nameUpdatedSecrets = updatedSecrets.filter(({ newSecretName }) => Boolean(newSecretName));
|
||||
const { keyName2BlindIndex: newKeyName2BlindIndex } = await fnSecretBlindIndexCheck({
|
||||
inputSecrets: nameUpdatedSecrets.map(({ newSecretName }) => ({ secretName: newSecretName as string })),
|
||||
const { keyName2BlindIndex: newKeyName2BlindIndex } = await secretService.fnSecretBlindIndexCheck({
|
||||
inputSecrets: nameUpdatedSecrets,
|
||||
folderId,
|
||||
isNew: true,
|
||||
blindIndexCfg,
|
||||
@@ -596,14 +563,14 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
const secretId = secsGroupedByBlindIndex[keyName2BlindIndex[secretName]][0].id;
|
||||
const secretBlindIndex =
|
||||
newSecretName && newKeyName2BlindIndex[newSecretName]
|
||||
? newKeyName2BlindIndex?.[newSecretName]
|
||||
? newKeyName2BlindIndex?.[secretName]
|
||||
: keyName2BlindIndex[secretName];
|
||||
// add tags
|
||||
if (tagIds?.length) commitTagIds[keyName2BlindIndex[secretName]] = tagIds;
|
||||
return {
|
||||
...latestSecretVersions[secretId],
|
||||
...el,
|
||||
op: SecretOperations.Update as const,
|
||||
op: CommitType.Update as const,
|
||||
secret: secretId,
|
||||
secretVersion: latestSecretVersions[secretId].id,
|
||||
secretBlindIndex,
|
||||
@@ -613,12 +580,12 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
);
|
||||
}
|
||||
// deleted secrets
|
||||
const deletedSecrets = data[SecretOperations.Delete];
|
||||
const deletedSecrets = data[CommitType.Delete];
|
||||
if (deletedSecrets && deletedSecrets.length) {
|
||||
// get all blind index
|
||||
// Find all those secrets
|
||||
// if not throw not found
|
||||
const { keyName2BlindIndex, secrets } = await fnSecretBlindIndexCheck({
|
||||
const { keyName2BlindIndex, secrets } = await secretService.fnSecretBlindIndexCheck({
|
||||
inputSecrets: deletedSecrets,
|
||||
folderId,
|
||||
isNew: false,
|
||||
@@ -639,7 +606,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
if (!latestSecretVersions[secretId].secretBlindIndex)
|
||||
throw new BadRequestError({ message: "Failed to find secret blind index" });
|
||||
return {
|
||||
op: SecretOperations.Delete as const,
|
||||
op: CommitType.Delete as const,
|
||||
...latestSecretVersions[secretId],
|
||||
secretBlindIndex: latestSecretVersions[secretId].secretBlindIndex as string,
|
||||
secret: secretId,
|
||||
@@ -663,7 +630,7 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
policyId: policy.id,
|
||||
status: "open",
|
||||
hasMerged: false,
|
||||
committerId: membership.id
|
||||
committerUserId: actorId
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user