mirror of
https://github.com/Infisical/infisical.git
synced 2025-07-13 09:35:39 +00:00
Compare commits
1 Commits
infisical/
...
create-pul
Author | SHA1 | Date | |
---|---|---|---|
77953ab1ba |
65
.env.example
65
.env.example
@ -1,65 +0,0 @@
|
|||||||
# Keys
|
|
||||||
# Required key for platform encryption/decryption ops
|
|
||||||
# THIS IS A SAMPLE ENCRYPTION KEY AND SHOULD NEVER BE USED FOR PRODUCTION
|
|
||||||
ENCRYPTION_KEY=6c1fe4e407b8911c104518103505b218
|
|
||||||
|
|
||||||
# JWT
|
|
||||||
# Required secrets to sign JWT tokens
|
|
||||||
# THIS IS A SAMPLE AUTH_SECRET KEY AND SHOULD NEVER BE USED FOR PRODUCTION
|
|
||||||
AUTH_SECRET=5lrMXKKWCVocS/uerPsl7V+TX/aaUaI7iDkgl3tSmLE=
|
|
||||||
|
|
||||||
# Postgres creds
|
|
||||||
POSTGRES_PASSWORD=infisical
|
|
||||||
POSTGRES_USER=infisical
|
|
||||||
POSTGRES_DB=infisical
|
|
||||||
|
|
||||||
# Required
|
|
||||||
DB_CONNECTION_URI=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
|
|
||||||
|
|
||||||
# Redis
|
|
||||||
REDIS_URL=redis://redis:6379
|
|
||||||
|
|
||||||
# Website URL
|
|
||||||
# Required
|
|
||||||
SITE_URL=http://localhost:8080
|
|
||||||
|
|
||||||
# Mail/SMTP
|
|
||||||
SMTP_HOST=
|
|
||||||
SMTP_PORT=
|
|
||||||
SMTP_NAME=
|
|
||||||
SMTP_USERNAME=
|
|
||||||
SMTP_PASSWORD=
|
|
||||||
|
|
||||||
# Integration
|
|
||||||
# Optional only if integration is used
|
|
||||||
CLIENT_ID_HEROKU=
|
|
||||||
CLIENT_ID_VERCEL=
|
|
||||||
CLIENT_ID_NETLIFY=
|
|
||||||
CLIENT_ID_GITHUB=
|
|
||||||
CLIENT_ID_GITLAB=
|
|
||||||
CLIENT_ID_BITBUCKET=
|
|
||||||
CLIENT_SECRET_HEROKU=
|
|
||||||
CLIENT_SECRET_VERCEL=
|
|
||||||
CLIENT_SECRET_NETLIFY=
|
|
||||||
CLIENT_SECRET_GITHUB=
|
|
||||||
CLIENT_SECRET_GITLAB=
|
|
||||||
CLIENT_SECRET_BITBUCKET=
|
|
||||||
CLIENT_SLUG_VERCEL=
|
|
||||||
|
|
||||||
# Sentry (optional) for monitoring errors
|
|
||||||
SENTRY_DSN=
|
|
||||||
|
|
||||||
# Infisical Cloud-specific configs
|
|
||||||
# Ignore - Not applicable for self-hosted version
|
|
||||||
POSTHOG_HOST=
|
|
||||||
POSTHOG_PROJECT_API_KEY=
|
|
||||||
|
|
||||||
# SSO-specific variables
|
|
||||||
CLIENT_ID_GOOGLE_LOGIN=
|
|
||||||
CLIENT_SECRET_GOOGLE_LOGIN=
|
|
||||||
|
|
||||||
CLIENT_ID_GITHUB_LOGIN=
|
|
||||||
CLIENT_SECRET_GITHUB_LOGIN=
|
|
||||||
|
|
||||||
CLIENT_ID_GITLAB_LOGIN=
|
|
||||||
CLIENT_SECRET_GITLAB_LOGIN=
|
|
@ -74,21 +74,21 @@ jobs:
|
|||||||
uses: pr-mpt/actions-commit-hash@v2
|
uses: pr-mpt/actions-commit-hash@v2
|
||||||
- name: Download task definition
|
- name: Download task definition
|
||||||
run: |
|
run: |
|
||||||
aws ecs describe-task-definition --task-definition infisical-core-platform --query taskDefinition > task-definition.json
|
aws ecs describe-task-definition --task-definition infisical-prod-platform --query taskDefinition > task-definition.json
|
||||||
- name: Render Amazon ECS task definition
|
- name: Render Amazon ECS task definition
|
||||||
id: render-web-container
|
id: render-web-container
|
||||||
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
||||||
with:
|
with:
|
||||||
task-definition: task-definition.json
|
task-definition: task-definition.json
|
||||||
container-name: infisical-core-platform
|
container-name: infisical-prod-platform
|
||||||
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||||
environment-variables: "LOG_LEVEL=info"
|
environment-variables: "LOG_LEVEL=info"
|
||||||
- name: Deploy to Amazon ECS service
|
- name: Deploy to Amazon ECS service
|
||||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
||||||
with:
|
with:
|
||||||
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
||||||
service: infisical-core-platform
|
service: infisical-prod-platform
|
||||||
cluster: infisical-core-platform
|
cluster: infisical-prod-platform
|
||||||
wait-for-service-stability: true
|
wait-for-service-stability: true
|
||||||
|
|
||||||
production-postgres-deployment:
|
production-postgres-deployment:
|
||||||
@ -122,19 +122,19 @@ jobs:
|
|||||||
uses: pr-mpt/actions-commit-hash@v2
|
uses: pr-mpt/actions-commit-hash@v2
|
||||||
- name: Download task definition
|
- name: Download task definition
|
||||||
run: |
|
run: |
|
||||||
aws ecs describe-task-definition --task-definition infisical-core-platform --query taskDefinition > task-definition.json
|
aws ecs describe-task-definition --task-definition infisical-prod-platform --query taskDefinition > task-definition.json
|
||||||
- name: Render Amazon ECS task definition
|
- name: Render Amazon ECS task definition
|
||||||
id: render-web-container
|
id: render-web-container
|
||||||
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
||||||
with:
|
with:
|
||||||
task-definition: task-definition.json
|
task-definition: task-definition.json
|
||||||
container-name: infisical-core-platform
|
container-name: infisical-prod-platform
|
||||||
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||||
environment-variables: "LOG_LEVEL=info"
|
environment-variables: "LOG_LEVEL=info"
|
||||||
- name: Deploy to Amazon ECS service
|
- name: Deploy to Amazon ECS service
|
||||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
||||||
with:
|
with:
|
||||||
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
||||||
service: infisical-core-platform
|
service: infisical-prod-platform
|
||||||
cluster: infisical-core-platform
|
cluster: infisical-prod-platform
|
||||||
wait-for-service-stability: true
|
wait-for-service-stability: true
|
||||||
|
@ -40,14 +40,13 @@ jobs:
|
|||||||
REDIS_URL: redis://172.17.0.1:6379
|
REDIS_URL: redis://172.17.0.1:6379
|
||||||
DB_CONNECTION_URI: postgres://infisical:infisical@172.17.0.1:5432/infisical?sslmode=disable
|
DB_CONNECTION_URI: postgres://infisical:infisical@172.17.0.1:5432/infisical?sslmode=disable
|
||||||
JWT_AUTH_SECRET: something-random
|
JWT_AUTH_SECRET: something-random
|
||||||
ENCRYPTION_KEY: 4bnfe4e407b8921c104518903515b218
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.21.5'
|
go-version: '1.21.5'
|
||||||
- name: Wait for container to be stable and check logs
|
- name: Wait for container to be stable and check logs
|
||||||
run: |
|
run: |
|
||||||
SECONDS=0
|
SECONDS=0
|
||||||
r HEALTHY=0
|
HEALTHY=0
|
||||||
while [ $SECONDS -lt 60 ]; do
|
while [ $SECONDS -lt 60 ]; do
|
||||||
if docker ps | grep infisical-api | grep -q healthy; then
|
if docker ps | grep infisical-api | grep -q healthy; then
|
||||||
echo "Container is healthy."
|
echo "Container is healthy."
|
||||||
@ -74,4 +73,4 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
docker-compose -f "docker-compose.dev.yml" down
|
docker-compose -f "docker-compose.dev.yml" down
|
||||||
docker stop infisical-api
|
docker stop infisical-api
|
||||||
docker remove infisical-api
|
docker remove infisical-api
|
@ -38,16 +38,6 @@ jobs:
|
|||||||
rm added_files.txt
|
rm added_files.txt
|
||||||
git commit -m "chore: renamed new migration files to latest timestamp (gh-action)"
|
git commit -m "chore: renamed new migration files to latest timestamp (gh-action)"
|
||||||
|
|
||||||
- name: Get PR details
|
|
||||||
id: pr_details
|
|
||||||
run: |
|
|
||||||
PR_NUMBER=${{ github.event.pull_request.number }}
|
|
||||||
PR_MERGER=$(curl -s "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" | jq -r '.merged_by.login')
|
|
||||||
|
|
||||||
echo "PR Number: $PR_NUMBER"
|
|
||||||
echo "PR Merger: $PR_MERGER"
|
|
||||||
echo "pr_merger=$PR_MERGER" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
if: env.SKIP_RENAME != 'true'
|
if: env.SKIP_RENAME != 'true'
|
||||||
uses: peter-evans/create-pull-request@v6
|
uses: peter-evans/create-pull-request@v6
|
||||||
@ -56,4 +46,3 @@ jobs:
|
|||||||
commit-message: 'chore: renamed new migration files to latest UTC (gh-action)'
|
commit-message: 'chore: renamed new migration files to latest UTC (gh-action)'
|
||||||
title: 'GH Action: rename new migration file timestamp'
|
title: 'GH Action: rename new migration file timestamp'
|
||||||
branch-suffix: timestamp
|
branch-suffix: timestamp
|
||||||
reviewers: ${{ steps.pr_details.outputs.pr_merger }}
|
|
||||||
|
@ -2,6 +2,4 @@
|
|||||||
frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/IdentityRbacSection.tsx:generic-api-key:206
|
frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/IdentityRbacSection.tsx:generic-api-key:206
|
||||||
frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:304
|
frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:304
|
||||||
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/MemberRbacSection.tsx:generic-api-key:206
|
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/MemberRbacSection.tsx:generic-api-key:206
|
||||||
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:292
|
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:292
|
||||||
docs/self-hosting/configuration/envars.mdx:generic-api-key:106
|
|
||||||
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:451
|
|
@ -55,7 +55,6 @@ VOLUME /app/.next/cache/images
|
|||||||
COPY --chown=non-root-user:nodejs --chmod=555 frontend/scripts ./scripts
|
COPY --chown=non-root-user:nodejs --chmod=555 frontend/scripts ./scripts
|
||||||
COPY --from=frontend-builder /app/public ./public
|
COPY --from=frontend-builder /app/public ./public
|
||||||
RUN chown non-root-user:nodejs ./public/data
|
RUN chown non-root-user:nodejs ./public/data
|
||||||
|
|
||||||
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/standalone ./
|
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/standalone ./
|
||||||
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/static ./.next/static
|
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
@ -94,18 +93,9 @@ RUN mkdir frontend-build
|
|||||||
|
|
||||||
# Production stage
|
# Production stage
|
||||||
FROM base AS production
|
FROM base AS production
|
||||||
RUN apk add --upgrade --no-cache ca-certificates
|
|
||||||
RUN addgroup --system --gid 1001 nodejs \
|
RUN addgroup --system --gid 1001 nodejs \
|
||||||
&& adduser --system --uid 1001 non-root-user
|
&& adduser --system --uid 1001 non-root-user
|
||||||
|
|
||||||
# Give non-root-user permission to update SSL certs
|
|
||||||
RUN chown -R non-root-user /etc/ssl/certs
|
|
||||||
RUN chown non-root-user /etc/ssl/certs/ca-certificates.crt
|
|
||||||
RUN chmod -R u+rwx /etc/ssl/certs
|
|
||||||
RUN chmod u+rw /etc/ssl/certs/ca-certificates.crt
|
|
||||||
RUN chown non-root-user /usr/sbin/update-ca-certificates
|
|
||||||
RUN chmod u+rx /usr/sbin/update-ca-certificates
|
|
||||||
|
|
||||||
## set pre baked keys
|
## set pre baked keys
|
||||||
ARG POSTHOG_API_KEY
|
ARG POSTHOG_API_KEY
|
||||||
ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
|
ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { Lock } from "@app/lib/red-lock";
|
|
||||||
|
|
||||||
export const mockKeyStore = (): TKeyStoreFactory => {
|
export const mockKeyStore = (): TKeyStoreFactory => {
|
||||||
const store: Record<string, string | number | Buffer> = {};
|
const store: Record<string, string | number | Buffer> = {};
|
||||||
@ -26,12 +25,6 @@ export const mockKeyStore = (): TKeyStoreFactory => {
|
|||||||
},
|
},
|
||||||
incrementBy: async () => {
|
incrementBy: async () => {
|
||||||
return 1;
|
return 1;
|
||||||
},
|
}
|
||||||
acquireLock: () => {
|
|
||||||
return Promise.resolve({
|
|
||||||
release: () => {}
|
|
||||||
}) as Promise<Lock>;
|
|
||||||
},
|
|
||||||
waitTillReady: async () => {}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
925
backend/package-lock.json
generated
925
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -95,13 +95,11 @@
|
|||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"axios-retry": "^4.0.0",
|
"axios-retry": "^4.0.0",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"bullmq": "^5.4.2",
|
"bullmq": "^5.3.3",
|
||||||
"cassandra-driver": "^4.7.2",
|
"cassandra-driver": "^4.7.2",
|
||||||
"dotenv": "^16.4.1",
|
"dotenv": "^16.4.1",
|
||||||
"fastify": "^4.26.0",
|
"fastify": "^4.26.0",
|
||||||
"fastify-plugin": "^4.5.1",
|
"fastify-plugin": "^4.5.1",
|
||||||
"google-auth-library": "^9.9.0",
|
|
||||||
"googleapis": "^137.1.0",
|
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
"ioredis": "^5.3.2",
|
"ioredis": "^5.3.2",
|
||||||
"jmespath": "^0.16.0",
|
"jmespath": "^0.16.0",
|
||||||
@ -112,7 +110,7 @@
|
|||||||
"libsodium-wrappers": "^0.7.13",
|
"libsodium-wrappers": "^0.7.13",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
"ms": "^2.1.3",
|
"ms": "^2.1.3",
|
||||||
"mysql2": "^3.9.8",
|
"mysql2": "^3.9.4",
|
||||||
"nanoid": "^5.0.4",
|
"nanoid": "^5.0.4",
|
||||||
"nodemailer": "^6.9.9",
|
"nodemailer": "^6.9.9",
|
||||||
"ora": "^7.0.1",
|
"ora": "^7.0.1",
|
||||||
|
@ -35,8 +35,6 @@ const getZodPrimitiveType = (type: string) => {
|
|||||||
return "z.coerce.number()";
|
return "z.coerce.number()";
|
||||||
case "text":
|
case "text":
|
||||||
return "z.string()";
|
return "z.string()";
|
||||||
case "bytea":
|
|
||||||
return "zodBuffer";
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Invalid type: ${type}`);
|
throw new Error(`Invalid type: ${type}`);
|
||||||
}
|
}
|
||||||
@ -98,15 +96,10 @@ const main = async () => {
|
|||||||
const columnNames = Object.keys(columns);
|
const columnNames = Object.keys(columns);
|
||||||
|
|
||||||
let schema = "";
|
let schema = "";
|
||||||
const zodImportSet = new Set<string>();
|
|
||||||
for (let colNum = 0; colNum < columnNames.length; colNum++) {
|
for (let colNum = 0; colNum < columnNames.length; colNum++) {
|
||||||
const columnName = columnNames[colNum];
|
const columnName = columnNames[colNum];
|
||||||
const colInfo = columns[columnName];
|
const colInfo = columns[columnName];
|
||||||
let ztype = getZodPrimitiveType(colInfo.type);
|
let ztype = getZodPrimitiveType(colInfo.type);
|
||||||
if (["zodBuffer"].includes(ztype)) {
|
|
||||||
zodImportSet.add(ztype);
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't put optional on id
|
// don't put optional on id
|
||||||
if (colInfo.defaultValue && columnName !== "id") {
|
if (colInfo.defaultValue && columnName !== "id") {
|
||||||
const { defaultValue } = colInfo;
|
const { defaultValue } = colInfo;
|
||||||
@ -128,8 +121,6 @@ const main = async () => {
|
|||||||
.split("_")
|
.split("_")
|
||||||
.reduce((prev, curr) => prev + `${curr.at(0)?.toUpperCase()}${curr.slice(1).toLowerCase()}`, "");
|
.reduce((prev, curr) => prev + `${curr.at(0)?.toUpperCase()}${curr.slice(1).toLowerCase()}`, "");
|
||||||
|
|
||||||
const zodImports = Array.from(zodImportSet);
|
|
||||||
|
|
||||||
// the insert and update are changed to zod input type to use default cases
|
// the insert and update are changed to zod input type to use default cases
|
||||||
writeFileSync(
|
writeFileSync(
|
||||||
path.join(__dirname, "../src/db/schemas", `${dashcase}.ts`),
|
path.join(__dirname, "../src/db/schemas", `${dashcase}.ts`),
|
||||||
@ -140,8 +131,6 @@ const main = async () => {
|
|||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
${zodImports.length ? `import { ${zodImports.join(",")} } from \"@app/lib/zod\";` : ""}
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
export const ${pascalCase}Schema = z.object({${schema}});
|
export const ${pascalCase}Schema = z.object({${schema}});
|
||||||
|
18
backend/src/@types/fastify.d.ts
vendored
18
backend/src/@types/fastify.d.ts
vendored
@ -1,11 +1,8 @@
|
|||||||
import "fastify";
|
import "fastify";
|
||||||
|
|
||||||
import { TUsers } from "@app/db/schemas";
|
import { TUsers } from "@app/db/schemas";
|
||||||
import { TAccessApprovalPolicyServiceFactory } from "@app/ee/services/access-approval-policy/access-approval-policy-service";
|
|
||||||
import { TAccessApprovalRequestServiceFactory } from "@app/ee/services/access-approval-request/access-approval-request-service";
|
|
||||||
import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
|
import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
|
||||||
import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types";
|
import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { TAuditLogStreamServiceFactory } from "@app/ee/services/audit-log-stream/audit-log-stream-service";
|
|
||||||
import { TDynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service";
|
import { TDynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service";
|
||||||
import { TDynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
|
import { TDynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
|
||||||
import { TGroupServiceFactory } from "@app/ee/services/group/group-service";
|
import { TGroupServiceFactory } from "@app/ee/services/group/group-service";
|
||||||
@ -32,10 +29,6 @@ import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-se
|
|||||||
import { TGroupProjectServiceFactory } from "@app/services/group-project/group-project-service";
|
import { TGroupProjectServiceFactory } from "@app/services/group-project/group-project-service";
|
||||||
import { TIdentityServiceFactory } from "@app/services/identity/identity-service";
|
import { TIdentityServiceFactory } from "@app/services/identity/identity-service";
|
||||||
import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
|
import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
|
||||||
import { TIdentityAwsAuthServiceFactory } from "@app/services/identity-aws-auth/identity-aws-auth-service";
|
|
||||||
import { TIdentityAzureAuthServiceFactory } from "@app/services/identity-azure-auth/identity-azure-auth-service";
|
|
||||||
import { TIdentityGcpAuthServiceFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-service";
|
|
||||||
import { TIdentityKubernetesAuthServiceFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-service";
|
|
||||||
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
||||||
import { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
|
import { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
|
||||||
import { TIntegrationServiceFactory } from "@app/services/integration/integration-service";
|
import { TIntegrationServiceFactory } from "@app/services/integration/integration-service";
|
||||||
@ -52,8 +45,6 @@ import { TSecretServiceFactory } from "@app/services/secret/secret-service";
|
|||||||
import { TSecretBlindIndexServiceFactory } from "@app/services/secret-blind-index/secret-blind-index-service";
|
import { TSecretBlindIndexServiceFactory } from "@app/services/secret-blind-index/secret-blind-index-service";
|
||||||
import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
|
import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
|
||||||
import { TSecretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
|
import { TSecretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
|
||||||
import { TSecretReplicationServiceFactory } from "@app/services/secret-replication/secret-replication-service";
|
|
||||||
import { TSecretSharingServiceFactory } from "@app/services/secret-sharing/secret-sharing-service";
|
|
||||||
import { TSecretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service";
|
import { TSecretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service";
|
||||||
import { TServiceTokenServiceFactory } from "@app/services/service-token/service-token-service";
|
import { TServiceTokenServiceFactory } from "@app/services/service-token/service-token-service";
|
||||||
import { TSuperAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
|
import { TSuperAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
|
||||||
@ -109,7 +100,6 @@ declare module "fastify" {
|
|||||||
projectKey: TProjectKeyServiceFactory;
|
projectKey: TProjectKeyServiceFactory;
|
||||||
projectRole: TProjectRoleServiceFactory;
|
projectRole: TProjectRoleServiceFactory;
|
||||||
secret: TSecretServiceFactory;
|
secret: TSecretServiceFactory;
|
||||||
secretReplication: TSecretReplicationServiceFactory;
|
|
||||||
secretTag: TSecretTagServiceFactory;
|
secretTag: TSecretTagServiceFactory;
|
||||||
secretImport: TSecretImportServiceFactory;
|
secretImport: TSecretImportServiceFactory;
|
||||||
projectBot: TProjectBotServiceFactory;
|
projectBot: TProjectBotServiceFactory;
|
||||||
@ -122,12 +112,6 @@ declare module "fastify" {
|
|||||||
identityAccessToken: TIdentityAccessTokenServiceFactory;
|
identityAccessToken: TIdentityAccessTokenServiceFactory;
|
||||||
identityProject: TIdentityProjectServiceFactory;
|
identityProject: TIdentityProjectServiceFactory;
|
||||||
identityUa: TIdentityUaServiceFactory;
|
identityUa: TIdentityUaServiceFactory;
|
||||||
identityKubernetesAuth: TIdentityKubernetesAuthServiceFactory;
|
|
||||||
identityGcpAuth: TIdentityGcpAuthServiceFactory;
|
|
||||||
identityAwsAuth: TIdentityAwsAuthServiceFactory;
|
|
||||||
identityAzureAuth: TIdentityAzureAuthServiceFactory;
|
|
||||||
accessApprovalPolicy: TAccessApprovalPolicyServiceFactory;
|
|
||||||
accessApprovalRequest: TAccessApprovalRequestServiceFactory;
|
|
||||||
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
|
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
|
||||||
secretApprovalRequest: TSecretApprovalRequestServiceFactory;
|
secretApprovalRequest: TSecretApprovalRequestServiceFactory;
|
||||||
secretRotation: TSecretRotationServiceFactory;
|
secretRotation: TSecretRotationServiceFactory;
|
||||||
@ -136,7 +120,6 @@ declare module "fastify" {
|
|||||||
scim: TScimServiceFactory;
|
scim: TScimServiceFactory;
|
||||||
ldap: TLdapConfigServiceFactory;
|
ldap: TLdapConfigServiceFactory;
|
||||||
auditLog: TAuditLogServiceFactory;
|
auditLog: TAuditLogServiceFactory;
|
||||||
auditLogStream: TAuditLogStreamServiceFactory;
|
|
||||||
secretScanning: TSecretScanningServiceFactory;
|
secretScanning: TSecretScanningServiceFactory;
|
||||||
license: TLicenseServiceFactory;
|
license: TLicenseServiceFactory;
|
||||||
trustedIp: TTrustedIpServiceFactory;
|
trustedIp: TTrustedIpServiceFactory;
|
||||||
@ -146,7 +129,6 @@ declare module "fastify" {
|
|||||||
dynamicSecretLease: TDynamicSecretLeaseServiceFactory;
|
dynamicSecretLease: TDynamicSecretLeaseServiceFactory;
|
||||||
projectUserAdditionalPrivilege: TProjectUserAdditionalPrivilegeServiceFactory;
|
projectUserAdditionalPrivilege: TProjectUserAdditionalPrivilegeServiceFactory;
|
||||||
identityProjectAdditionalPrivilege: TIdentityProjectAdditionalPrivilegeServiceFactory;
|
identityProjectAdditionalPrivilege: TIdentityProjectAdditionalPrivilegeServiceFactory;
|
||||||
secretSharing: TSecretSharingServiceFactory;
|
|
||||||
};
|
};
|
||||||
// this is exclusive use for middlewares in which we need to inject data
|
// this is exclusive use for middlewares in which we need to inject data
|
||||||
// everywhere else access using service layer
|
// everywhere else access using service layer
|
||||||
|
106
backend/src/@types/knex.d.ts
vendored
106
backend/src/@types/knex.d.ts
vendored
@ -2,26 +2,11 @@ import { Knex } from "knex";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
TableName,
|
TableName,
|
||||||
TAccessApprovalPolicies,
|
|
||||||
TAccessApprovalPoliciesApprovers,
|
|
||||||
TAccessApprovalPoliciesApproversInsert,
|
|
||||||
TAccessApprovalPoliciesApproversUpdate,
|
|
||||||
TAccessApprovalPoliciesInsert,
|
|
||||||
TAccessApprovalPoliciesUpdate,
|
|
||||||
TAccessApprovalRequests,
|
|
||||||
TAccessApprovalRequestsInsert,
|
|
||||||
TAccessApprovalRequestsReviewers,
|
|
||||||
TAccessApprovalRequestsReviewersInsert,
|
|
||||||
TAccessApprovalRequestsReviewersUpdate,
|
|
||||||
TAccessApprovalRequestsUpdate,
|
|
||||||
TApiKeys,
|
TApiKeys,
|
||||||
TApiKeysInsert,
|
TApiKeysInsert,
|
||||||
TApiKeysUpdate,
|
TApiKeysUpdate,
|
||||||
TAuditLogs,
|
TAuditLogs,
|
||||||
TAuditLogsInsert,
|
TAuditLogsInsert,
|
||||||
TAuditLogStreams,
|
|
||||||
TAuditLogStreamsInsert,
|
|
||||||
TAuditLogStreamsUpdate,
|
|
||||||
TAuditLogsUpdate,
|
TAuditLogsUpdate,
|
||||||
TAuthTokens,
|
TAuthTokens,
|
||||||
TAuthTokenSessions,
|
TAuthTokenSessions,
|
||||||
@ -59,18 +44,6 @@ import {
|
|||||||
TIdentityAccessTokens,
|
TIdentityAccessTokens,
|
||||||
TIdentityAccessTokensInsert,
|
TIdentityAccessTokensInsert,
|
||||||
TIdentityAccessTokensUpdate,
|
TIdentityAccessTokensUpdate,
|
||||||
TIdentityAwsAuths,
|
|
||||||
TIdentityAwsAuthsInsert,
|
|
||||||
TIdentityAwsAuthsUpdate,
|
|
||||||
TIdentityAzureAuths,
|
|
||||||
TIdentityAzureAuthsInsert,
|
|
||||||
TIdentityAzureAuthsUpdate,
|
|
||||||
TIdentityGcpAuths,
|
|
||||||
TIdentityGcpAuthsInsert,
|
|
||||||
TIdentityGcpAuthsUpdate,
|
|
||||||
TIdentityKubernetesAuths,
|
|
||||||
TIdentityKubernetesAuthsInsert,
|
|
||||||
TIdentityKubernetesAuthsUpdate,
|
|
||||||
TIdentityOrgMemberships,
|
TIdentityOrgMemberships,
|
||||||
TIdentityOrgMembershipsInsert,
|
TIdentityOrgMembershipsInsert,
|
||||||
TIdentityOrgMembershipsUpdate,
|
TIdentityOrgMembershipsUpdate,
|
||||||
@ -98,15 +71,6 @@ import {
|
|||||||
TIntegrations,
|
TIntegrations,
|
||||||
TIntegrationsInsert,
|
TIntegrationsInsert,
|
||||||
TIntegrationsUpdate,
|
TIntegrationsUpdate,
|
||||||
TKmsKeys,
|
|
||||||
TKmsKeysInsert,
|
|
||||||
TKmsKeysUpdate,
|
|
||||||
TKmsKeyVersions,
|
|
||||||
TKmsKeyVersionsInsert,
|
|
||||||
TKmsKeyVersionsUpdate,
|
|
||||||
TKmsRootConfig,
|
|
||||||
TKmsRootConfigInsert,
|
|
||||||
TKmsRootConfigUpdate,
|
|
||||||
TLdapConfigs,
|
TLdapConfigs,
|
||||||
TLdapConfigsInsert,
|
TLdapConfigsInsert,
|
||||||
TLdapConfigsUpdate,
|
TLdapConfigsUpdate,
|
||||||
@ -185,9 +149,6 @@ import {
|
|||||||
TSecretImports,
|
TSecretImports,
|
||||||
TSecretImportsInsert,
|
TSecretImportsInsert,
|
||||||
TSecretImportsUpdate,
|
TSecretImportsUpdate,
|
||||||
TSecretReferences,
|
|
||||||
TSecretReferencesInsert,
|
|
||||||
TSecretReferencesUpdate,
|
|
||||||
TSecretRotationOutputs,
|
TSecretRotationOutputs,
|
||||||
TSecretRotationOutputsInsert,
|
TSecretRotationOutputsInsert,
|
||||||
TSecretRotationOutputsUpdate,
|
TSecretRotationOutputsUpdate,
|
||||||
@ -198,9 +159,6 @@ import {
|
|||||||
TSecretScanningGitRisks,
|
TSecretScanningGitRisks,
|
||||||
TSecretScanningGitRisksInsert,
|
TSecretScanningGitRisksInsert,
|
||||||
TSecretScanningGitRisksUpdate,
|
TSecretScanningGitRisksUpdate,
|
||||||
TSecretSharing,
|
|
||||||
TSecretSharingInsert,
|
|
||||||
TSecretSharingUpdate,
|
|
||||||
TSecretsInsert,
|
TSecretsInsert,
|
||||||
TSecretSnapshotFolders,
|
TSecretSnapshotFolders,
|
||||||
TSecretSnapshotFoldersInsert,
|
TSecretSnapshotFoldersInsert,
|
||||||
@ -325,11 +283,6 @@ declare module "knex/types/tables" {
|
|||||||
>;
|
>;
|
||||||
[TableName.ProjectKeys]: Knex.CompositeTableType<TProjectKeys, TProjectKeysInsert, TProjectKeysUpdate>;
|
[TableName.ProjectKeys]: Knex.CompositeTableType<TProjectKeys, TProjectKeysInsert, TProjectKeysUpdate>;
|
||||||
[TableName.Secret]: Knex.CompositeTableType<TSecrets, TSecretsInsert, TSecretsUpdate>;
|
[TableName.Secret]: Knex.CompositeTableType<TSecrets, TSecretsInsert, TSecretsUpdate>;
|
||||||
[TableName.SecretReference]: Knex.CompositeTableType<
|
|
||||||
TSecretReferences,
|
|
||||||
TSecretReferencesInsert,
|
|
||||||
TSecretReferencesUpdate
|
|
||||||
>;
|
|
||||||
[TableName.SecretBlindIndex]: Knex.CompositeTableType<
|
[TableName.SecretBlindIndex]: Knex.CompositeTableType<
|
||||||
TSecretBlindIndexes,
|
TSecretBlindIndexes,
|
||||||
TSecretBlindIndexesInsert,
|
TSecretBlindIndexesInsert,
|
||||||
@ -342,7 +295,6 @@ declare module "knex/types/tables" {
|
|||||||
TSecretFolderVersionsInsert,
|
TSecretFolderVersionsInsert,
|
||||||
TSecretFolderVersionsUpdate
|
TSecretFolderVersionsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.SecretSharing]: Knex.CompositeTableType<TSecretSharing, TSecretSharingInsert, TSecretSharingUpdate>;
|
|
||||||
[TableName.SecretTag]: Knex.CompositeTableType<TSecretTags, TSecretTagsInsert, TSecretTagsUpdate>;
|
[TableName.SecretTag]: Knex.CompositeTableType<TSecretTags, TSecretTagsInsert, TSecretTagsUpdate>;
|
||||||
[TableName.SecretImport]: Knex.CompositeTableType<TSecretImports, TSecretImportsInsert, TSecretImportsUpdate>;
|
[TableName.SecretImport]: Knex.CompositeTableType<TSecretImports, TSecretImportsInsert, TSecretImportsUpdate>;
|
||||||
[TableName.Integration]: Knex.CompositeTableType<TIntegrations, TIntegrationsInsert, TIntegrationsUpdate>;
|
[TableName.Integration]: Knex.CompositeTableType<TIntegrations, TIntegrationsInsert, TIntegrationsUpdate>;
|
||||||
@ -359,26 +311,6 @@ declare module "knex/types/tables" {
|
|||||||
TIdentityUniversalAuthsInsert,
|
TIdentityUniversalAuthsInsert,
|
||||||
TIdentityUniversalAuthsUpdate
|
TIdentityUniversalAuthsUpdate
|
||||||
>;
|
>;
|
||||||
[TableName.IdentityKubernetesAuth]: Knex.CompositeTableType<
|
|
||||||
TIdentityKubernetesAuths,
|
|
||||||
TIdentityKubernetesAuthsInsert,
|
|
||||||
TIdentityKubernetesAuthsUpdate
|
|
||||||
>;
|
|
||||||
[TableName.IdentityGcpAuth]: Knex.CompositeTableType<
|
|
||||||
TIdentityGcpAuths,
|
|
||||||
TIdentityGcpAuthsInsert,
|
|
||||||
TIdentityGcpAuthsUpdate
|
|
||||||
>;
|
|
||||||
[TableName.IdentityAwsAuth]: Knex.CompositeTableType<
|
|
||||||
TIdentityAwsAuths,
|
|
||||||
TIdentityAwsAuthsInsert,
|
|
||||||
TIdentityAwsAuthsUpdate
|
|
||||||
>;
|
|
||||||
[TableName.IdentityAzureAuth]: Knex.CompositeTableType<
|
|
||||||
TIdentityAzureAuths,
|
|
||||||
TIdentityAzureAuthsInsert,
|
|
||||||
TIdentityAzureAuthsUpdate
|
|
||||||
>;
|
|
||||||
[TableName.IdentityUaClientSecret]: Knex.CompositeTableType<
|
[TableName.IdentityUaClientSecret]: Knex.CompositeTableType<
|
||||||
TIdentityUaClientSecrets,
|
TIdentityUaClientSecrets,
|
||||||
TIdentityUaClientSecretsInsert,
|
TIdentityUaClientSecretsInsert,
|
||||||
@ -409,31 +341,6 @@ declare module "knex/types/tables" {
|
|||||||
TIdentityProjectAdditionalPrivilegeInsert,
|
TIdentityProjectAdditionalPrivilegeInsert,
|
||||||
TIdentityProjectAdditionalPrivilegeUpdate
|
TIdentityProjectAdditionalPrivilegeUpdate
|
||||||
>;
|
>;
|
||||||
|
|
||||||
[TableName.AccessApprovalPolicy]: Knex.CompositeTableType<
|
|
||||||
TAccessApprovalPolicies,
|
|
||||||
TAccessApprovalPoliciesInsert,
|
|
||||||
TAccessApprovalPoliciesUpdate
|
|
||||||
>;
|
|
||||||
|
|
||||||
[TableName.AccessApprovalPolicyApprover]: Knex.CompositeTableType<
|
|
||||||
TAccessApprovalPoliciesApprovers,
|
|
||||||
TAccessApprovalPoliciesApproversInsert,
|
|
||||||
TAccessApprovalPoliciesApproversUpdate
|
|
||||||
>;
|
|
||||||
|
|
||||||
[TableName.AccessApprovalRequest]: Knex.CompositeTableType<
|
|
||||||
TAccessApprovalRequests,
|
|
||||||
TAccessApprovalRequestsInsert,
|
|
||||||
TAccessApprovalRequestsUpdate
|
|
||||||
>;
|
|
||||||
|
|
||||||
[TableName.AccessApprovalRequestReviewer]: Knex.CompositeTableType<
|
|
||||||
TAccessApprovalRequestsReviewers,
|
|
||||||
TAccessApprovalRequestsReviewersInsert,
|
|
||||||
TAccessApprovalRequestsReviewersUpdate
|
|
||||||
>;
|
|
||||||
|
|
||||||
[TableName.ScimToken]: Knex.CompositeTableType<TScimTokens, TScimTokensInsert, TScimTokensUpdate>;
|
[TableName.ScimToken]: Knex.CompositeTableType<TScimTokens, TScimTokensInsert, TScimTokensUpdate>;
|
||||||
[TableName.SecretApprovalPolicy]: Knex.CompositeTableType<
|
[TableName.SecretApprovalPolicy]: Knex.CompositeTableType<
|
||||||
TSecretApprovalPolicies,
|
TSecretApprovalPolicies,
|
||||||
@ -497,11 +404,6 @@ declare module "knex/types/tables" {
|
|||||||
[TableName.LdapGroupMap]: Knex.CompositeTableType<TLdapGroupMaps, TLdapGroupMapsInsert, TLdapGroupMapsUpdate>;
|
[TableName.LdapGroupMap]: Knex.CompositeTableType<TLdapGroupMaps, TLdapGroupMapsInsert, TLdapGroupMapsUpdate>;
|
||||||
[TableName.OrgBot]: Knex.CompositeTableType<TOrgBots, TOrgBotsInsert, TOrgBotsUpdate>;
|
[TableName.OrgBot]: Knex.CompositeTableType<TOrgBots, TOrgBotsInsert, TOrgBotsUpdate>;
|
||||||
[TableName.AuditLog]: Knex.CompositeTableType<TAuditLogs, TAuditLogsInsert, TAuditLogsUpdate>;
|
[TableName.AuditLog]: Knex.CompositeTableType<TAuditLogs, TAuditLogsInsert, TAuditLogsUpdate>;
|
||||||
[TableName.AuditLogStream]: Knex.CompositeTableType<
|
|
||||||
TAuditLogStreams,
|
|
||||||
TAuditLogStreamsInsert,
|
|
||||||
TAuditLogStreamsUpdate
|
|
||||||
>;
|
|
||||||
[TableName.GitAppInstallSession]: Knex.CompositeTableType<
|
[TableName.GitAppInstallSession]: Knex.CompositeTableType<
|
||||||
TGitAppInstallSessions,
|
TGitAppInstallSessions,
|
||||||
TGitAppInstallSessionsInsert,
|
TGitAppInstallSessionsInsert,
|
||||||
@ -525,13 +427,5 @@ declare module "knex/types/tables" {
|
|||||||
TSecretVersionTagJunctionInsert,
|
TSecretVersionTagJunctionInsert,
|
||||||
TSecretVersionTagJunctionUpdate
|
TSecretVersionTagJunctionUpdate
|
||||||
>;
|
>;
|
||||||
// KMS service
|
|
||||||
[TableName.KmsServerRootConfig]: Knex.CompositeTableType<
|
|
||||||
TKmsRootConfig,
|
|
||||||
TKmsRootConfigInsert,
|
|
||||||
TKmsRootConfigUpdate
|
|
||||||
>;
|
|
||||||
[TableName.KmsKey]: Knex.CompositeTableType<TKmsKeys, TKmsKeysInsert, TKmsKeysUpdate>;
|
|
||||||
[TableName.KmsKeyVersion]: Knex.CompositeTableType<TKmsKeyVersions, TKmsKeyVersionsInsert, TKmsKeyVersionsUpdate>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,28 +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");
|
|
||||||
const doesCreatedAtExist = await knex.schema.hasColumn(TableName.AuditLog, "createdAt");
|
|
||||||
if (await knex.schema.hasTable(TableName.AuditLog)) {
|
|
||||||
await knex.schema.alterTable(TableName.AuditLog, (t) => {
|
|
||||||
if (doesProjectIdExist && doesCreatedAtExist) t.index(["projectId", "createdAt"]);
|
|
||||||
if (doesOrgIdExist && doesCreatedAtExist) t.index(["orgId", "createdAt"]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
const doesCreatedAtExist = await knex.schema.hasColumn(TableName.AuditLog, "createdAt");
|
|
||||||
|
|
||||||
if (await knex.schema.hasTable(TableName.AuditLog)) {
|
|
||||||
await knex.schema.alterTable(TableName.AuditLog, (t) => {
|
|
||||||
if (doesProjectIdExist && doesCreatedAtExist) t.dropIndex(["projectId", "createdAt"]);
|
|
||||||
if (doesOrgIdExist && doesCreatedAtExist) t.dropIndex(["orgId", "createdAt"]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1 @@
|
|||||||
|
|
@ -1,28 +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.AuditLogStream))) {
|
|
||||||
await knex.schema.createTable(TableName.AuditLogStream, (t) => {
|
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
|
||||||
t.string("url").notNullable();
|
|
||||||
t.text("encryptedHeadersCiphertext");
|
|
||||||
t.text("encryptedHeadersIV");
|
|
||||||
t.text("encryptedHeadersTag");
|
|
||||||
t.string("encryptedHeadersAlgorithm");
|
|
||||||
t.string("encryptedHeadersKeyEncoding");
|
|
||||||
t.uuid("orgId").notNullable();
|
|
||||||
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
|
||||||
t.timestamps(true, true, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await createOnUpdateTrigger(knex, TableName.AuditLogStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
await dropOnUpdateTrigger(knex, TableName.AuditLogStream);
|
|
||||||
await knex.schema.dropTableIfExists(TableName.AuditLogStream);
|
|
||||||
}
|
|
@ -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,41 +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.AccessApprovalPolicy))) {
|
|
||||||
await knex.schema.createTable(TableName.AccessApprovalPolicy, (t) => {
|
|
||||||
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.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
|
|
||||||
t.timestamps(true, true, true);
|
|
||||||
});
|
|
||||||
await createOnUpdateTrigger(knex, TableName.AccessApprovalPolicy);
|
|
||||||
}
|
|
||||||
|
|
||||||
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("policyId").notNullable();
|
|
||||||
t.foreign("policyId").references("id").inTable(TableName.AccessApprovalPolicy).onDelete("CASCADE");
|
|
||||||
t.timestamps(true, true, true);
|
|
||||||
});
|
|
||||||
await createOnUpdateTrigger(knex, TableName.AccessApprovalPolicyApprover);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.dropTableIfExists(TableName.AccessApprovalPolicyApprover);
|
|
||||||
await knex.schema.dropTableIfExists(TableName.AccessApprovalPolicy);
|
|
||||||
|
|
||||||
await dropOnUpdateTrigger(knex, TableName.AccessApprovalPolicyApprover);
|
|
||||||
await dropOnUpdateTrigger(knex, TableName.AccessApprovalPolicy);
|
|
||||||
}
|
|
@ -1,51 +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.AccessApprovalRequest))) {
|
|
||||||
await knex.schema.createTable(TableName.AccessApprovalRequest, (t) => {
|
|
||||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
|
||||||
|
|
||||||
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("requestedBy").notNullable();
|
|
||||||
t.foreign("requestedBy").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
|
||||||
|
|
||||||
// We use these values to create the actual privilege at a later point in time.
|
|
||||||
t.boolean("isTemporary").notNullable();
|
|
||||||
t.string("temporaryRange").nullable();
|
|
||||||
|
|
||||||
t.jsonb("permissions").notNullable();
|
|
||||||
|
|
||||||
t.timestamps(true, true, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
await createOnUpdateTrigger(knex, TableName.AccessApprovalRequest);
|
|
||||||
|
|
||||||
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.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
|
||||||
await knex.schema.dropTableIfExists(TableName.AccessApprovalRequestReviewer);
|
|
||||||
await knex.schema.dropTableIfExists(TableName.AccessApprovalRequest);
|
|
||||||
|
|
||||||
await dropOnUpdateTrigger(knex, TableName.AccessApprovalRequestReviewer);
|
|
||||||
await dropOnUpdateTrigger(knex, TableName.AccessApprovalRequest);
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
@ -1,25 +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 AccessApprovalPoliciesApproversSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
approverId: z.string().uuid(),
|
|
||||||
policyId: z.string().uuid(),
|
|
||||||
createdAt: z.date(),
|
|
||||||
updatedAt: z.date()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TAccessApprovalPoliciesApprovers = z.infer<typeof AccessApprovalPoliciesApproversSchema>;
|
|
||||||
export type TAccessApprovalPoliciesApproversInsert = Omit<
|
|
||||||
z.input<typeof AccessApprovalPoliciesApproversSchema>,
|
|
||||||
TImmutableDBKeys
|
|
||||||
>;
|
|
||||||
export type TAccessApprovalPoliciesApproversUpdate = Partial<
|
|
||||||
Omit<z.input<typeof AccessApprovalPoliciesApproversSchema>, 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 AccessApprovalRequestsReviewersSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
member: z.string().uuid(),
|
|
||||||
status: z.string(),
|
|
||||||
requestId: z.string().uuid(),
|
|
||||||
createdAt: z.date(),
|
|
||||||
updatedAt: z.date()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TAccessApprovalRequestsReviewers = z.infer<typeof AccessApprovalRequestsReviewersSchema>;
|
|
||||||
export type TAccessApprovalRequestsReviewersInsert = Omit<
|
|
||||||
z.input<typeof AccessApprovalRequestsReviewersSchema>,
|
|
||||||
TImmutableDBKeys
|
|
||||||
>;
|
|
||||||
export type TAccessApprovalRequestsReviewersUpdate = Partial<
|
|
||||||
Omit<z.input<typeof AccessApprovalRequestsReviewersSchema>, 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 AccessApprovalRequestsSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
policyId: z.string().uuid(),
|
|
||||||
privilegeId: z.string().uuid().nullable().optional(),
|
|
||||||
requestedBy: z.string().uuid(),
|
|
||||||
isTemporary: z.boolean(),
|
|
||||||
temporaryRange: z.string().nullable().optional(),
|
|
||||||
permissions: z.unknown(),
|
|
||||||
createdAt: z.date(),
|
|
||||||
updatedAt: z.date()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TAccessApprovalRequests = z.infer<typeof AccessApprovalRequestsSchema>;
|
|
||||||
export type TAccessApprovalRequestsInsert = Omit<z.input<typeof AccessApprovalRequestsSchema>, TImmutableDBKeys>;
|
|
||||||
export type TAccessApprovalRequestsUpdate = Partial<
|
|
||||||
Omit<z.input<typeof AccessApprovalRequestsSchema>, TImmutableDBKeys>
|
|
||||||
>;
|
|
@ -1,25 +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 AuditLogStreamsSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
url: z.string(),
|
|
||||||
encryptedHeadersCiphertext: z.string().nullable().optional(),
|
|
||||||
encryptedHeadersIV: z.string().nullable().optional(),
|
|
||||||
encryptedHeadersTag: z.string().nullable().optional(),
|
|
||||||
encryptedHeadersAlgorithm: z.string().nullable().optional(),
|
|
||||||
encryptedHeadersKeyEncoding: z.string().nullable().optional(),
|
|
||||||
orgId: z.string().uuid(),
|
|
||||||
createdAt: z.date(),
|
|
||||||
updatedAt: z.date()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TAuditLogStreams = z.infer<typeof AuditLogStreamsSchema>;
|
|
||||||
export type TAuditLogStreamsInsert = Omit<z.input<typeof AuditLogStreamsSchema>, TImmutableDBKeys>;
|
|
||||||
export type TAuditLogStreamsUpdate = Partial<Omit<z.input<typeof AuditLogStreamsSchema>, 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>
|
|
||||||
>;
|
|
@ -1,9 +1,4 @@
|
|||||||
export * from "./access-approval-policies";
|
|
||||||
export * from "./access-approval-policies-approvers";
|
|
||||||
export * from "./access-approval-requests";
|
|
||||||
export * from "./access-approval-requests-reviewers";
|
|
||||||
export * from "./api-keys";
|
export * from "./api-keys";
|
||||||
export * from "./audit-log-streams";
|
|
||||||
export * from "./audit-logs";
|
export * from "./audit-logs";
|
||||||
export * from "./auth-token-sessions";
|
export * from "./auth-token-sessions";
|
||||||
export * from "./auth-tokens";
|
export * from "./auth-tokens";
|
||||||
@ -17,10 +12,6 @@ export * from "./group-project-memberships";
|
|||||||
export * from "./groups";
|
export * from "./groups";
|
||||||
export * from "./identities";
|
export * from "./identities";
|
||||||
export * from "./identity-access-tokens";
|
export * from "./identity-access-tokens";
|
||||||
export * from "./identity-aws-auths";
|
|
||||||
export * from "./identity-azure-auths";
|
|
||||||
export * from "./identity-gcp-auths";
|
|
||||||
export * from "./identity-kubernetes-auths";
|
|
||||||
export * from "./identity-org-memberships";
|
export * from "./identity-org-memberships";
|
||||||
export * from "./identity-project-additional-privilege";
|
export * from "./identity-project-additional-privilege";
|
||||||
export * from "./identity-project-membership-role";
|
export * from "./identity-project-membership-role";
|
||||||
@ -30,9 +21,6 @@ export * from "./identity-universal-auths";
|
|||||||
export * from "./incident-contacts";
|
export * from "./incident-contacts";
|
||||||
export * from "./integration-auths";
|
export * from "./integration-auths";
|
||||||
export * from "./integrations";
|
export * from "./integrations";
|
||||||
export * from "./kms-key-versions";
|
|
||||||
export * from "./kms-keys";
|
|
||||||
export * from "./kms-root-config";
|
|
||||||
export * from "./ldap-configs";
|
export * from "./ldap-configs";
|
||||||
export * from "./ldap-group-maps";
|
export * from "./ldap-group-maps";
|
||||||
export * from "./models";
|
export * from "./models";
|
||||||
@ -60,11 +48,9 @@ export * from "./secret-blind-indexes";
|
|||||||
export * from "./secret-folder-versions";
|
export * from "./secret-folder-versions";
|
||||||
export * from "./secret-folders";
|
export * from "./secret-folders";
|
||||||
export * from "./secret-imports";
|
export * from "./secret-imports";
|
||||||
export * from "./secret-references";
|
|
||||||
export * from "./secret-rotation-outputs";
|
export * from "./secret-rotation-outputs";
|
||||||
export * from "./secret-rotations";
|
export * from "./secret-rotations";
|
||||||
export * from "./secret-scanning-git-risks";
|
export * from "./secret-scanning-git-risks";
|
||||||
export * from "./secret-sharing";
|
|
||||||
export * from "./secret-snapshot-folders";
|
export * from "./secret-snapshot-folders";
|
||||||
export * from "./secret-snapshot-secrets";
|
export * from "./secret-snapshot-secrets";
|
||||||
export * from "./secret-snapshots";
|
export * from "./secret-snapshots";
|
||||||
|
@ -28,10 +28,7 @@ export const IntegrationsSchema = z.object({
|
|||||||
secretPath: z.string().default("/"),
|
secretPath: z.string().default("/"),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
lastUsed: z.date().nullable().optional(),
|
lastUsed: z.date().nullable().optional()
|
||||||
isSynced: z.boolean().nullable().optional(),
|
|
||||||
syncMessage: z.string().nullable().optional(),
|
|
||||||
lastSyncJobId: z.string().nullable().optional()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIntegrations = z.infer<typeof IntegrationsSchema>;
|
export type TIntegrations = z.infer<typeof IntegrationsSchema>;
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
// Code generated by automation script, DO NOT EDIT.
|
|
||||||
// Automated by pulling database and generating zod schema
|
|
||||||
// To update. Just run npm run generate:schema
|
|
||||||
// Written by akhilmhdh.
|
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { zodBuffer } from "@app/lib/zod";
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
|
||||||
|
|
||||||
export const KmsKeyVersionsSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
encryptedKey: zodBuffer,
|
|
||||||
version: z.number(),
|
|
||||||
kmsKeyId: z.string().uuid()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TKmsKeyVersions = z.infer<typeof KmsKeyVersionsSchema>;
|
|
||||||
export type TKmsKeyVersionsInsert = Omit<z.input<typeof KmsKeyVersionsSchema>, TImmutableDBKeys>;
|
|
||||||
export type TKmsKeyVersionsUpdate = Partial<Omit<z.input<typeof KmsKeyVersionsSchema>, TImmutableDBKeys>>;
|
|
@ -1,26 +0,0 @@
|
|||||||
// Code generated by automation script, DO NOT EDIT.
|
|
||||||
// Automated by pulling database and generating zod schema
|
|
||||||
// To update. Just run npm run generate:schema
|
|
||||||
// Written by akhilmhdh.
|
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { zodBuffer } from "@app/lib/zod";
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
|
||||||
|
|
||||||
export const KmsKeysSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
encryptedKey: zodBuffer,
|
|
||||||
encryptionAlgorithm: z.string(),
|
|
||||||
version: z.number().default(1),
|
|
||||||
description: z.string().nullable().optional(),
|
|
||||||
isDisabled: z.boolean().default(false).nullable().optional(),
|
|
||||||
isReserved: z.boolean().default(true).nullable().optional(),
|
|
||||||
projectId: z.string().nullable().optional(),
|
|
||||||
orgId: z.string().uuid().nullable().optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TKmsKeys = z.infer<typeof KmsKeysSchema>;
|
|
||||||
export type TKmsKeysInsert = Omit<z.input<typeof KmsKeysSchema>, TImmutableDBKeys>;
|
|
||||||
export type TKmsKeysUpdate = Partial<Omit<z.input<typeof KmsKeysSchema>, TImmutableDBKeys>>;
|
|
@ -1,19 +0,0 @@
|
|||||||
// Code generated by automation script, DO NOT EDIT.
|
|
||||||
// Automated by pulling database and generating zod schema
|
|
||||||
// To update. Just run npm run generate:schema
|
|
||||||
// Written by akhilmhdh.
|
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { zodBuffer } from "@app/lib/zod";
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
|
||||||
|
|
||||||
export const KmsRootConfigSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
encryptedRootKey: zodBuffer
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TKmsRootConfig = z.infer<typeof KmsRootConfigSchema>;
|
|
||||||
export type TKmsRootConfigInsert = Omit<z.input<typeof KmsRootConfigSchema>, TImmutableDBKeys>;
|
|
||||||
export type TKmsRootConfigUpdate = Partial<Omit<z.input<typeof KmsRootConfigSchema>, TImmutableDBKeys>>;
|
|
@ -28,8 +28,6 @@ export enum TableName {
|
|||||||
ProjectUserMembershipRole = "project_user_membership_roles",
|
ProjectUserMembershipRole = "project_user_membership_roles",
|
||||||
ProjectKeys = "project_keys",
|
ProjectKeys = "project_keys",
|
||||||
Secret = "secrets",
|
Secret = "secrets",
|
||||||
SecretReference = "secret_references",
|
|
||||||
SecretSharing = "secret_sharing",
|
|
||||||
SecretBlindIndex = "secret_blind_indexes",
|
SecretBlindIndex = "secret_blind_indexes",
|
||||||
SecretVersion = "secret_versions",
|
SecretVersion = "secret_versions",
|
||||||
SecretFolder = "secret_folders",
|
SecretFolder = "secret_folders",
|
||||||
@ -46,20 +44,12 @@ export enum TableName {
|
|||||||
Identity = "identities",
|
Identity = "identities",
|
||||||
IdentityAccessToken = "identity_access_tokens",
|
IdentityAccessToken = "identity_access_tokens",
|
||||||
IdentityUniversalAuth = "identity_universal_auths",
|
IdentityUniversalAuth = "identity_universal_auths",
|
||||||
IdentityKubernetesAuth = "identity_kubernetes_auths",
|
|
||||||
IdentityGcpAuth = "identity_gcp_auths",
|
|
||||||
IdentityAzureAuth = "identity_azure_auths",
|
|
||||||
IdentityUaClientSecret = "identity_ua_client_secrets",
|
IdentityUaClientSecret = "identity_ua_client_secrets",
|
||||||
IdentityAwsAuth = "identity_aws_auths",
|
|
||||||
IdentityOrgMembership = "identity_org_memberships",
|
IdentityOrgMembership = "identity_org_memberships",
|
||||||
IdentityProjectMembership = "identity_project_memberships",
|
IdentityProjectMembership = "identity_project_memberships",
|
||||||
IdentityProjectMembershipRole = "identity_project_membership_role",
|
IdentityProjectMembershipRole = "identity_project_membership_role",
|
||||||
IdentityProjectAdditionalPrivilege = "identity_project_additional_privilege",
|
IdentityProjectAdditionalPrivilege = "identity_project_additional_privilege",
|
||||||
ScimToken = "scim_tokens",
|
ScimToken = "scim_tokens",
|
||||||
AccessApprovalPolicy = "access_approval_policies",
|
|
||||||
AccessApprovalPolicyApprover = "access_approval_policies_approvers",
|
|
||||||
AccessApprovalRequest = "access_approval_requests",
|
|
||||||
AccessApprovalRequestReviewer = "access_approval_requests_reviewers",
|
|
||||||
SecretApprovalPolicy = "secret_approval_policies",
|
SecretApprovalPolicy = "secret_approval_policies",
|
||||||
SecretApprovalPolicyApprover = "secret_approval_policies_approvers",
|
SecretApprovalPolicyApprover = "secret_approval_policies_approvers",
|
||||||
SecretApprovalRequest = "secret_approval_requests",
|
SecretApprovalRequest = "secret_approval_requests",
|
||||||
@ -72,7 +62,6 @@ export enum TableName {
|
|||||||
LdapConfig = "ldap_configs",
|
LdapConfig = "ldap_configs",
|
||||||
LdapGroupMap = "ldap_group_maps",
|
LdapGroupMap = "ldap_group_maps",
|
||||||
AuditLog = "audit_logs",
|
AuditLog = "audit_logs",
|
||||||
AuditLogStream = "audit_log_streams",
|
|
||||||
GitAppInstallSession = "git_app_install_sessions",
|
GitAppInstallSession = "git_app_install_sessions",
|
||||||
GitAppOrg = "git_app_org",
|
GitAppOrg = "git_app_org",
|
||||||
SecretScanningGitRisk = "secret_scanning_git_risks",
|
SecretScanningGitRisk = "secret_scanning_git_risks",
|
||||||
@ -81,11 +70,7 @@ export enum TableName {
|
|||||||
DynamicSecretLease = "dynamic_secret_leases",
|
DynamicSecretLease = "dynamic_secret_leases",
|
||||||
// junction tables with tags
|
// junction tables with tags
|
||||||
JnSecretTag = "secret_tag_junction",
|
JnSecretTag = "secret_tag_junction",
|
||||||
SecretVersionTag = "secret_version_tag_junction",
|
SecretVersionTag = "secret_version_tag_junction"
|
||||||
// KMS Service
|
|
||||||
KmsServerRootConfig = "kms_root_config",
|
|
||||||
KmsKey = "kms_keys",
|
|
||||||
KmsKeyVersion = "kms_key_versions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
|
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
|
||||||
@ -152,9 +137,5 @@ export enum ProjectUpgradeStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum IdentityAuthMethod {
|
export enum IdentityAuthMethod {
|
||||||
Univeral = "universal-auth",
|
Univeral = "universal-auth"
|
||||||
KUBERNETES_AUTH = "kubernetes-auth",
|
|
||||||
GCP_AUTH = "gcp-auth",
|
|
||||||
AWS_AUTH = "aws-auth",
|
|
||||||
AZURE_AUTH = "azure-auth"
|
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,7 @@ export const SecretApprovalRequestsSchema = z.object({
|
|||||||
statusChangeBy: z.string().uuid().nullable().optional(),
|
statusChangeBy: z.string().uuid().nullable().optional(),
|
||||||
committerId: z.string().uuid(),
|
committerId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date()
|
||||||
isReplicated: z.boolean().nullable().optional()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretApprovalRequests = z.infer<typeof SecretApprovalRequestsSchema>;
|
export type TSecretApprovalRequests = z.infer<typeof SecretApprovalRequestsSchema>;
|
||||||
|
@ -14,8 +14,7 @@ export const SecretFoldersSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
envId: z.string().uuid(),
|
envId: z.string().uuid(),
|
||||||
parentId: z.string().uuid().nullable().optional(),
|
parentId: z.string().uuid().nullable().optional()
|
||||||
isReserved: z.boolean().default(false).nullable().optional()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretFolders = z.infer<typeof SecretFoldersSchema>;
|
export type TSecretFolders = z.infer<typeof SecretFoldersSchema>;
|
||||||
|
@ -15,12 +15,7 @@ export const SecretImportsSchema = z.object({
|
|||||||
position: z.number(),
|
position: z.number(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
folderId: z.string().uuid(),
|
folderId: z.string().uuid()
|
||||||
isReplication: z.boolean().default(false).nullable().optional(),
|
|
||||||
isReplicationSuccess: z.boolean().nullable().optional(),
|
|
||||||
replicationStatus: z.string().nullable().optional(),
|
|
||||||
lastReplicated: z.date().nullable().optional(),
|
|
||||||
isReserved: z.boolean().default(false).nullable().optional()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretImports = z.infer<typeof SecretImportsSchema>;
|
export type TSecretImports = z.infer<typeof SecretImportsSchema>;
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
// Code generated by automation script, DO NOT EDIT.
|
|
||||||
// Automated by pulling database and generating zod schema
|
|
||||||
// To update. Just run npm run generate:schema
|
|
||||||
// Written by akhilmhdh.
|
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
|
||||||
|
|
||||||
export const SecretReferencesSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
environment: z.string(),
|
|
||||||
secretPath: z.string(),
|
|
||||||
secretId: z.string().uuid(),
|
|
||||||
createdAt: z.date(),
|
|
||||||
updatedAt: z.date()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TSecretReferences = z.infer<typeof SecretReferencesSchema>;
|
|
||||||
export type TSecretReferencesInsert = Omit<z.input<typeof SecretReferencesSchema>, TImmutableDBKeys>;
|
|
||||||
export type TSecretReferencesUpdate = Partial<Omit<z.input<typeof SecretReferencesSchema>, TImmutableDBKeys>>;
|
|
@ -1,26 +0,0 @@
|
|||||||
// Code generated by automation script, DO NOT EDIT.
|
|
||||||
// Automated by pulling database and generating zod schema
|
|
||||||
// To update. Just run npm run generate:schema
|
|
||||||
// Written by akhilmhdh.
|
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
|
||||||
|
|
||||||
export const SecretSharingSchema = z.object({
|
|
||||||
id: z.string().uuid(),
|
|
||||||
encryptedValue: z.string(),
|
|
||||||
iv: z.string(),
|
|
||||||
tag: z.string(),
|
|
||||||
hashedHex: z.string(),
|
|
||||||
expiresAt: z.date(),
|
|
||||||
userId: z.string().uuid(),
|
|
||||||
orgId: z.string().uuid(),
|
|
||||||
createdAt: z.date(),
|
|
||||||
updatedAt: z.date(),
|
|
||||||
expiresAfterViews: z.number().nullable().optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
export type TSecretSharing = z.infer<typeof SecretSharingSchema>;
|
|
||||||
export type TSecretSharingInsert = Omit<z.input<typeof SecretSharingSchema>, TImmutableDBKeys>;
|
|
||||||
export type TSecretSharingUpdate = Partial<Omit<z.input<typeof SecretSharingSchema>, TImmutableDBKeys>>;
|
|
@ -14,9 +14,7 @@ export const SuperAdminSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
allowedSignUpDomain: z.string().nullable().optional(),
|
allowedSignUpDomain: z.string().nullable().optional(),
|
||||||
instanceId: z.string().uuid().default("00000000-0000-0000-0000-000000000000"),
|
instanceId: z.string().uuid().default("00000000-0000-0000-0000-000000000000")
|
||||||
trustSamlEmails: z.boolean().default(false).nullable().optional(),
|
|
||||||
trustLdapEmails: z.boolean().default(false).nullable().optional()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
||||||
|
@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
export const UserAliasesSchema = z.object({
|
export const UserAliasesSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
userId: z.string().uuid(),
|
userId: z.string().uuid(),
|
||||||
username: z.string().nullable().optional(),
|
username: z.string(),
|
||||||
aliasType: z.string(),
|
aliasType: z.string(),
|
||||||
externalId: z.string(),
|
externalId: z.string(),
|
||||||
emails: z.string().array().nullable().optional(),
|
emails: z.string().array().nullable().optional(),
|
||||||
|
@ -21,11 +21,7 @@ export const UsersSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
isGhost: z.boolean().default(false),
|
isGhost: z.boolean().default(false),
|
||||||
username: z.string(),
|
username: z.string()
|
||||||
isEmailVerified: z.boolean().default(false).nullable().optional(),
|
|
||||||
consecutiveFailedMfaAttempts: z.number().default(0).nullable().optional(),
|
|
||||||
isLocked: z.boolean().default(false).nullable().optional(),
|
|
||||||
temporaryLockDateEnd: z.date().nullable().optional()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TUsers = z.infer<typeof UsersSchema>;
|
export type TUsers = z.infer<typeof UsersSchema>;
|
||||||
|
@ -1,168 +0,0 @@
|
|||||||
import { nanoid } from "nanoid";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
|
||||||
import { sapPubSchema } from "@app/server/routes/sanitizedSchemas";
|
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
|
||||||
|
|
||||||
export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvider) => {
|
|
||||||
server.route({
|
|
||||||
url: "/",
|
|
||||||
method: "POST",
|
|
||||||
schema: {
|
|
||||||
body: z
|
|
||||||
.object({
|
|
||||||
projectSlug: z.string().trim(),
|
|
||||||
name: z.string().optional(),
|
|
||||||
secretPath: z.string().trim().default("/"),
|
|
||||||
environment: z.string(),
|
|
||||||
approvers: z.string().array().min(1),
|
|
||||||
approvals: z.number().min(1).default(1)
|
|
||||||
})
|
|
||||||
.refine((data) => data.approvals <= data.approvers.length, {
|
|
||||||
path: ["approvals"],
|
|
||||||
message: "The number of approvals should be lower than the number of approvers."
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
approval: sapPubSchema
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const approval = await server.services.accessApprovalPolicy.createAccessApprovalPolicy({
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
...req.body,
|
|
||||||
projectSlug: req.body.projectSlug,
|
|
||||||
name: req.body.name ?? `${req.body.environment}-${nanoid(3)}`
|
|
||||||
});
|
|
||||||
return { approval };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
url: "/",
|
|
||||||
method: "GET",
|
|
||||||
schema: {
|
|
||||||
querystring: z.object({
|
|
||||||
projectSlug: z.string().trim()
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
approvals: sapPubSchema.extend({ approvers: z.string().array(), secretPath: z.string().optional() }).array()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const approvals = await server.services.accessApprovalPolicy.getAccessApprovalPolicyByProjectSlug({
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
projectSlug: req.query.projectSlug
|
|
||||||
});
|
|
||||||
return { approvals };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
url: "/count",
|
|
||||||
method: "GET",
|
|
||||||
schema: {
|
|
||||||
querystring: z.object({
|
|
||||||
projectSlug: z.string(),
|
|
||||||
envSlug: z.string()
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
count: z.number()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const { count } = await server.services.accessApprovalPolicy.getAccessPolicyCountByEnvSlug({
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
projectSlug: req.query.projectSlug,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
envSlug: req.query.envSlug
|
|
||||||
});
|
|
||||||
return { count };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
url: "/:policyId",
|
|
||||||
method: "PATCH",
|
|
||||||
schema: {
|
|
||||||
params: z.object({
|
|
||||||
policyId: z.string()
|
|
||||||
}),
|
|
||||||
body: z
|
|
||||||
.object({
|
|
||||||
name: z.string().optional(),
|
|
||||||
secretPath: z
|
|
||||||
.string()
|
|
||||||
.trim()
|
|
||||||
.optional()
|
|
||||||
.transform((val) => (val === "" ? "/" : val)),
|
|
||||||
approvers: z.string().array().min(1),
|
|
||||||
approvals: z.number().min(1).default(1)
|
|
||||||
})
|
|
||||||
.refine((data) => data.approvals <= data.approvers.length, {
|
|
||||||
path: ["approvals"],
|
|
||||||
message: "The number of approvals should be lower than the number of approvers."
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
approval: sapPubSchema
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
|
||||||
handler: async (req) => {
|
|
||||||
await server.services.accessApprovalPolicy.updateAccessApprovalPolicy({
|
|
||||||
policyId: req.params.policyId,
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
...req.body
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
url: "/:policyId",
|
|
||||||
method: "DELETE",
|
|
||||||
schema: {
|
|
||||||
params: z.object({
|
|
||||||
policyId: z.string()
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
approval: sapPubSchema
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const approval = await server.services.accessApprovalPolicy.deleteAccessApprovalPolicy({
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
policyId: req.params.policyId
|
|
||||||
});
|
|
||||||
return { approval };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,160 +0,0 @@
|
|||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { AccessApprovalRequestsReviewersSchema, AccessApprovalRequestsSchema } from "@app/db/schemas";
|
|
||||||
import { ApprovalStatus } from "@app/ee/services/access-approval-request/access-approval-request-types";
|
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
|
||||||
|
|
||||||
export const registerAccessApprovalRequestRouter = async (server: FastifyZodProvider) => {
|
|
||||||
server.route({
|
|
||||||
url: "/",
|
|
||||||
method: "POST",
|
|
||||||
schema: {
|
|
||||||
body: z.object({
|
|
||||||
permissions: z.any().array(),
|
|
||||||
isTemporary: z.boolean(),
|
|
||||||
temporaryRange: z.string().optional()
|
|
||||||
}),
|
|
||||||
querystring: z.object({
|
|
||||||
projectSlug: z.string().trim()
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
approval: AccessApprovalRequestsSchema
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const { request } = await server.services.accessApprovalRequest.createAccessApprovalRequest({
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
permissions: req.body.permissions,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
projectSlug: req.query.projectSlug,
|
|
||||||
temporaryRange: req.body.temporaryRange,
|
|
||||||
isTemporary: req.body.isTemporary
|
|
||||||
});
|
|
||||||
return { approval: request };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
url: "/count",
|
|
||||||
method: "GET",
|
|
||||||
schema: {
|
|
||||||
querystring: z.object({
|
|
||||||
projectSlug: z.string().trim()
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
pendingCount: z.number(),
|
|
||||||
finalizedCount: z.number()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const { count } = await server.services.accessApprovalRequest.getCount({
|
|
||||||
projectSlug: req.query.projectSlug,
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actorAuthMethod: req.permission.authMethod
|
|
||||||
});
|
|
||||||
|
|
||||||
return { ...count };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
url: "/",
|
|
||||||
method: "GET",
|
|
||||||
schema: {
|
|
||||||
querystring: z.object({
|
|
||||||
projectSlug: z.string().trim(),
|
|
||||||
authorProjectMembershipId: z.string().trim().optional(),
|
|
||||||
envSlug: z.string().trim().optional()
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
requests: AccessApprovalRequestsSchema.extend({
|
|
||||||
environmentName: z.string(),
|
|
||||||
isApproved: z.boolean(),
|
|
||||||
privilege: z
|
|
||||||
.object({
|
|
||||||
membershipId: z.string(),
|
|
||||||
isTemporary: z.boolean(),
|
|
||||||
temporaryMode: z.string().nullish(),
|
|
||||||
temporaryRange: z.string().nullish(),
|
|
||||||
temporaryAccessStartTime: z.date().nullish(),
|
|
||||||
temporaryAccessEndTime: z.date().nullish(),
|
|
||||||
permissions: z.unknown()
|
|
||||||
})
|
|
||||||
.nullable(),
|
|
||||||
policy: z.object({
|
|
||||||
id: z.string(),
|
|
||||||
name: z.string(),
|
|
||||||
approvals: z.number(),
|
|
||||||
approvers: z.string().array(),
|
|
||||||
secretPath: z.string().nullish(),
|
|
||||||
envId: z.string()
|
|
||||||
}),
|
|
||||||
reviewers: z
|
|
||||||
.object({
|
|
||||||
member: z.string(),
|
|
||||||
status: z.string()
|
|
||||||
})
|
|
||||||
.array()
|
|
||||||
}).array()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const { requests } = await server.services.accessApprovalRequest.listApprovalRequests({
|
|
||||||
projectSlug: req.query.projectSlug,
|
|
||||||
authorProjectMembershipId: req.query.authorProjectMembershipId,
|
|
||||||
envSlug: req.query.envSlug,
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actorAuthMethod: req.permission.authMethod
|
|
||||||
});
|
|
||||||
|
|
||||||
return { requests };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
url: "/:requestId/review",
|
|
||||||
method: "POST",
|
|
||||||
schema: {
|
|
||||||
params: z.object({
|
|
||||||
requestId: z.string().trim()
|
|
||||||
}),
|
|
||||||
body: z.object({
|
|
||||||
status: z.enum([ApprovalStatus.APPROVED, ApprovalStatus.REJECTED])
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
review: AccessApprovalRequestsReviewersSchema
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const review = await server.services.accessApprovalRequest.reviewAccessRequest({
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
requestId: req.params.requestId,
|
|
||||||
status: req.body.status
|
|
||||||
});
|
|
||||||
|
|
||||||
return { review };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,215 +0,0 @@
|
|||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { AUDIT_LOG_STREAMS } from "@app/lib/api-docs";
|
|
||||||
import { readLimit } from "@app/server/config/rateLimiter";
|
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
|
||||||
import { SanitizedAuditLogStreamSchema } from "@app/server/routes/sanitizedSchemas";
|
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
|
||||||
|
|
||||||
export const registerAuditLogStreamRouter = async (server: FastifyZodProvider) => {
|
|
||||||
server.route({
|
|
||||||
method: "POST",
|
|
||||||
url: "/",
|
|
||||||
config: {
|
|
||||||
rateLimit: readLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
description: "Create an Audit Log Stream.",
|
|
||||||
security: [
|
|
||||||
{
|
|
||||||
bearerAuth: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
body: z.object({
|
|
||||||
url: z.string().min(1).describe(AUDIT_LOG_STREAMS.CREATE.url),
|
|
||||||
headers: z
|
|
||||||
.object({
|
|
||||||
key: z.string().min(1).trim().describe(AUDIT_LOG_STREAMS.CREATE.headers.key),
|
|
||||||
value: z.string().min(1).trim().describe(AUDIT_LOG_STREAMS.CREATE.headers.value)
|
|
||||||
})
|
|
||||||
.describe(AUDIT_LOG_STREAMS.CREATE.headers.desc)
|
|
||||||
.array()
|
|
||||||
.optional()
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
auditLogStream: SanitizedAuditLogStreamSchema
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const auditLogStream = await server.services.auditLogStream.create({
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
url: req.body.url,
|
|
||||||
headers: req.body.headers
|
|
||||||
});
|
|
||||||
|
|
||||||
return { auditLogStream };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: "PATCH",
|
|
||||||
url: "/:id",
|
|
||||||
config: {
|
|
||||||
rateLimit: readLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
description: "Update an Audit Log Stream by ID.",
|
|
||||||
security: [
|
|
||||||
{
|
|
||||||
bearerAuth: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
params: z.object({
|
|
||||||
id: z.string().describe(AUDIT_LOG_STREAMS.UPDATE.id)
|
|
||||||
}),
|
|
||||||
body: z.object({
|
|
||||||
url: z.string().optional().describe(AUDIT_LOG_STREAMS.UPDATE.url),
|
|
||||||
headers: z
|
|
||||||
.object({
|
|
||||||
key: z.string().min(1).trim().describe(AUDIT_LOG_STREAMS.UPDATE.headers.key),
|
|
||||||
value: z.string().min(1).trim().describe(AUDIT_LOG_STREAMS.UPDATE.headers.value)
|
|
||||||
})
|
|
||||||
.describe(AUDIT_LOG_STREAMS.UPDATE.headers.desc)
|
|
||||||
.array()
|
|
||||||
.optional()
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
auditLogStream: SanitizedAuditLogStreamSchema
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const auditLogStream = await server.services.auditLogStream.updateById({
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
id: req.params.id,
|
|
||||||
url: req.body.url,
|
|
||||||
headers: req.body.headers
|
|
||||||
});
|
|
||||||
|
|
||||||
return { auditLogStream };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: "DELETE",
|
|
||||||
url: "/:id",
|
|
||||||
config: {
|
|
||||||
rateLimit: readLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
description: "Delete an Audit Log Stream by ID.",
|
|
||||||
security: [
|
|
||||||
{
|
|
||||||
bearerAuth: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
params: z.object({
|
|
||||||
id: z.string().describe(AUDIT_LOG_STREAMS.DELETE.id)
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
auditLogStream: SanitizedAuditLogStreamSchema
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const auditLogStream = await server.services.auditLogStream.deleteById({
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
id: req.params.id
|
|
||||||
});
|
|
||||||
|
|
||||||
return { auditLogStream };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: "GET",
|
|
||||||
url: "/:id",
|
|
||||||
config: {
|
|
||||||
rateLimit: readLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
description: "Get an Audit Log Stream by ID.",
|
|
||||||
security: [
|
|
||||||
{
|
|
||||||
bearerAuth: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
params: z.object({
|
|
||||||
id: z.string().describe(AUDIT_LOG_STREAMS.GET_BY_ID.id)
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
auditLogStream: SanitizedAuditLogStreamSchema.extend({
|
|
||||||
headers: z
|
|
||||||
.object({
|
|
||||||
key: z.string(),
|
|
||||||
value: z.string()
|
|
||||||
})
|
|
||||||
.array()
|
|
||||||
.optional()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const auditLogStream = await server.services.auditLogStream.getById({
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
id: req.params.id
|
|
||||||
});
|
|
||||||
|
|
||||||
return { auditLogStream };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: "GET",
|
|
||||||
url: "/",
|
|
||||||
config: {
|
|
||||||
rateLimit: readLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
description: "List Audit Log Streams.",
|
|
||||||
security: [
|
|
||||||
{
|
|
||||||
bearerAuth: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
auditLogStreams: SanitizedAuditLogStreamSchema.array()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const auditLogStreams = await server.services.auditLogStream.list({
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actorAuthMethod: req.permission.authMethod
|
|
||||||
});
|
|
||||||
|
|
||||||
return { auditLogStreams };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,19 +1,16 @@
|
|||||||
import { packRules } from "@casl/ability/extra";
|
import { MongoAbility, RawRuleOf } from "@casl/ability";
|
||||||
|
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { IdentityProjectAdditionalPrivilegeSchema } from "@app/db/schemas";
|
||||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
||||||
|
import { ProjectPermissionSet } from "@app/ee/services/permission/project-permission";
|
||||||
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import {
|
|
||||||
ProjectPermissionSchema,
|
|
||||||
ProjectSpecificPrivilegePermissionSchema,
|
|
||||||
SanitizedIdentityPrivilegeSchema
|
|
||||||
} from "@app/server/routes/sanitizedSchemas";
|
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
||||||
@ -44,33 +41,16 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||||
permissions: ProjectPermissionSchema.array()
|
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
|
||||||
.optional(),
|
|
||||||
privilegePermission: ProjectSpecificPrivilegePermissionSchema.describe(
|
|
||||||
IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.privilegePermission
|
|
||||||
).optional()
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: SanitizedIdentityPrivilegeSchema
|
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { permissions, privilegePermission } = req.body;
|
|
||||||
if (!permissions && !privilegePermission) {
|
|
||||||
throw new BadRequestError({ message: "Permission or privilegePermission must be provided" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const permission = privilegePermission
|
|
||||||
? privilegePermission.actions.map((action) => ({
|
|
||||||
action,
|
|
||||||
subject: privilegePermission.subject,
|
|
||||||
conditions: privilegePermission.conditions
|
|
||||||
}))
|
|
||||||
: permissions!;
|
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
|
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
@ -79,7 +59,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
...req.body,
|
...req.body,
|
||||||
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
||||||
isTemporary: false,
|
isTemporary: false,
|
||||||
permissions: JSON.stringify(packRules(permission))
|
permissions: JSON.stringify(packRules(req.body.permissions))
|
||||||
});
|
});
|
||||||
return { privilege };
|
return { privilege };
|
||||||
}
|
}
|
||||||
@ -112,12 +92,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||||
permissions: ProjectPermissionSchema.array()
|
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions),
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
|
||||||
.optional(),
|
|
||||||
privilegePermission: ProjectSpecificPrivilegePermissionSchema.describe(
|
|
||||||
IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.privilegePermission
|
|
||||||
).optional(),
|
|
||||||
temporaryMode: z
|
temporaryMode: z
|
||||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
||||||
@ -132,25 +107,12 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: SanitizedIdentityPrivilegeSchema
|
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { permissions, privilegePermission } = req.body;
|
|
||||||
if (!permissions && !privilegePermission) {
|
|
||||||
throw new BadRequestError({ message: "Permission or privilegePermission must be provided" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const permission = privilegePermission
|
|
||||||
? privilegePermission.actions.map((action) => ({
|
|
||||||
action,
|
|
||||||
subject: privilegePermission.subject,
|
|
||||||
conditions: privilegePermission.conditions
|
|
||||||
}))
|
|
||||||
: permissions!;
|
|
||||||
|
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
|
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
@ -159,7 +121,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
...req.body,
|
...req.body,
|
||||||
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
||||||
isTemporary: true,
|
isTemporary: true,
|
||||||
permissions: JSON.stringify(packRules(permission))
|
permissions: JSON.stringify(packRules(req.body.permissions))
|
||||||
});
|
});
|
||||||
return { privilege };
|
return { privilege };
|
||||||
}
|
}
|
||||||
@ -195,17 +157,14 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
message: "Slug must be a valid slug"
|
message: "Slug must be a valid slug"
|
||||||
})
|
})
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.newSlug),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.newSlug),
|
||||||
permissions: ProjectPermissionSchema.array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.permissions),
|
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.permissions),
|
||||||
privilegePermission: ProjectSpecificPrivilegePermissionSchema.describe(
|
|
||||||
IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.privilegePermission
|
|
||||||
).optional(),
|
|
||||||
isTemporary: z.boolean().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
|
isTemporary: z.boolean().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
|
||||||
temporaryMode: z
|
temporaryMode: z
|
||||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
|
||||||
temporaryRange: z
|
temporaryRange: z
|
||||||
.string()
|
.string()
|
||||||
.refine((val) => typeof val === "undefined" || ms(val) > 0, "Temporary range must be a positive number")
|
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryRange),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryRange),
|
||||||
temporaryAccessStartTime: z
|
temporaryAccessStartTime: z
|
||||||
.string()
|
.string()
|
||||||
@ -216,24 +175,13 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: SanitizedIdentityPrivilegeSchema
|
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { permissions, privilegePermission, ...updatedInfo } = req.body.privilegeDetails;
|
const updatedInfo = req.body.privilegeDetails;
|
||||||
if (!permissions && !privilegePermission) {
|
|
||||||
throw new BadRequestError({ message: "Permission or privilegePermission must be provided" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const permission = privilegePermission
|
|
||||||
? privilegePermission.actions.map((action) => ({
|
|
||||||
action,
|
|
||||||
subject: privilegePermission.subject,
|
|
||||||
conditions: privilegePermission.conditions
|
|
||||||
}))
|
|
||||||
: permissions!;
|
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilege.updateBySlug({
|
const privilege = await server.services.identityProjectAdditionalPrivilege.updateBySlug({
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
@ -244,7 +192,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
projectSlug: req.body.projectSlug,
|
projectSlug: req.body.projectSlug,
|
||||||
data: {
|
data: {
|
||||||
...updatedInfo,
|
...updatedInfo,
|
||||||
permissions: permission ? JSON.stringify(packRules(permission)) : undefined
|
permissions: updatedInfo?.permissions ? JSON.stringify(packRules(updatedInfo.permissions)) : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return { privilege };
|
return { privilege };
|
||||||
@ -271,7 +219,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: SanitizedIdentityPrivilegeSchema
|
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -312,7 +260,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: SanitizedIdentityPrivilegeSchema
|
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -345,11 +293,16 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
],
|
],
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.identityId),
|
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.identityId),
|
||||||
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.projectSlug)
|
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.projectSlug),
|
||||||
|
unpacked: z
|
||||||
|
.enum(["false", "true"])
|
||||||
|
.transform((el) => el === "true")
|
||||||
|
.default("true")
|
||||||
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.unpacked)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privileges: SanitizedIdentityPrivilegeSchema.array()
|
privileges: IdentityProjectAdditionalPrivilegeSchema.array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -362,9 +315,15 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
...req.query
|
...req.query
|
||||||
});
|
});
|
||||||
return {
|
if (req.query.unpacked) {
|
||||||
privileges
|
return {
|
||||||
};
|
privileges: privileges.map(({ permissions, ...el }) => ({
|
||||||
|
...el,
|
||||||
|
permissions: unpackRules(permissions as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[])
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { privileges };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
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 { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
|
||||||
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
|
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
|
||||||
import { registerGroupRouter } from "./group-router";
|
import { registerGroupRouter } from "./group-router";
|
||||||
@ -43,9 +40,6 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
|||||||
prefix: "/secret-rotation-providers"
|
prefix: "/secret-rotation-providers"
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.register(registerAccessApprovalPolicyRouter, { prefix: "/access-approvals/policies" });
|
|
||||||
await server.register(registerAccessApprovalRequestRouter, { prefix: "/access-approvals/requests" });
|
|
||||||
|
|
||||||
await server.register(
|
await server.register(
|
||||||
async (dynamicSecretRouter) => {
|
async (dynamicSecretRouter) => {
|
||||||
await dynamicSecretRouter.register(registerDynamicSecretRouter);
|
await dynamicSecretRouter.register(registerDynamicSecretRouter);
|
||||||
@ -61,7 +55,6 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
|||||||
await server.register(registerSecretRotationRouter, { prefix: "/secret-rotations" });
|
await server.register(registerSecretRotationRouter, { prefix: "/secret-rotations" });
|
||||||
await server.register(registerSecretVersionRouter, { prefix: "/secret" });
|
await server.register(registerSecretVersionRouter, { prefix: "/secret" });
|
||||||
await server.register(registerGroupRouter, { prefix: "/groups" });
|
await server.register(registerGroupRouter, { prefix: "/groups" });
|
||||||
await server.register(registerAuditLogStreamRouter, { prefix: "/audit-log-streams" });
|
|
||||||
await server.register(
|
await server.register(
|
||||||
async (privilegeRouter) => {
|
async (privilegeRouter) => {
|
||||||
await privilegeRouter.register(registerUserAdditionalPrivilegeRouter, { prefix: "/users" });
|
await privilegeRouter.register(registerUserAdditionalPrivilegeRouter, { prefix: "/users" });
|
||||||
|
@ -18,7 +18,6 @@ import { LdapConfigsSchema, LdapGroupMapsSchema } from "@app/db/schemas";
|
|||||||
import { TLDAPConfig } from "@app/ee/services/ldap-config/ldap-config-types";
|
import { TLDAPConfig } from "@app/ee/services/ldap-config/ldap-config-types";
|
||||||
import { isValidLdapFilter, searchGroups } from "@app/ee/services/ldap-config/ldap-fns";
|
import { isValidLdapFilter, searchGroups } from "@app/ee/services/ldap-config/ldap-fns";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
@ -53,7 +52,6 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
async (req: IncomingMessage, user, cb) => {
|
async (req: IncomingMessage, user, cb) => {
|
||||||
try {
|
try {
|
||||||
if (!user.email) throw new BadRequestError({ message: "Invalid request. Missing email." });
|
|
||||||
const ldapConfig = (req as unknown as FastifyRequest).ldapConfig as TLDAPConfig;
|
const ldapConfig = (req as unknown as FastifyRequest).ldapConfig as TLDAPConfig;
|
||||||
|
|
||||||
let groups: { dn: string; cn: string }[] | undefined;
|
let groups: { dn: string; cn: string }[] | undefined;
|
||||||
@ -76,7 +74,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
username: user.uid,
|
username: user.uid,
|
||||||
firstName: user.givenName ?? user.cn ?? "",
|
firstName: user.givenName ?? user.cn ?? "",
|
||||||
lastName: user.sn ?? "",
|
lastName: user.sn ?? "",
|
||||||
email: user.mail,
|
emails: user.mail ? [user.mail] : [],
|
||||||
groups,
|
groups,
|
||||||
relayState: ((req as unknown as FastifyRequest).body as { RelayState?: string }).RelayState,
|
relayState: ((req as unknown as FastifyRequest).body as { RelayState?: string }).RelayState,
|
||||||
orgId: (req as unknown as FastifyRequest).ldapConfig.organization
|
orgId: (req as unknown as FastifyRequest).ldapConfig.organization
|
||||||
|
@ -23,7 +23,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
.min(1)
|
.min(1)
|
||||||
.trim()
|
.trim()
|
||||||
.refine(
|
.refine(
|
||||||
(val) => !Object.values(OrgMembershipRole).includes(val as OrgMembershipRole),
|
(val) => !Object.keys(OrgMembershipRole).includes(val),
|
||||||
"Please choose a different slug, the slug you have entered is reserved"
|
"Please choose a different slug, the slug you have entered is reserved"
|
||||||
)
|
)
|
||||||
.refine((v) => slugify(v) === v, {
|
.refine((v) => slugify(v) === v, {
|
||||||
|
@ -1,232 +1,146 @@
|
|||||||
import { packRules } from "@casl/ability/extra";
|
|
||||||
import slugify from "@sindresorhus/slugify";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ProjectMembershipRole, ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
|
import { ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
|
||||||
import { PROJECT_ROLE } from "@app/lib/api-docs";
|
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { ProjectPermissionSchema, SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas";
|
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/:projectSlug/roles",
|
url: "/:projectId/roles",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: writeLimit
|
rateLimit: writeLimit
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
description: "Create a project role",
|
|
||||||
security: [
|
|
||||||
{
|
|
||||||
bearerAuth: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectSlug: z.string().trim().describe(PROJECT_ROLE.CREATE.projectSlug)
|
projectId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
slug: z
|
slug: z.string().trim(),
|
||||||
.string()
|
name: z.string().trim(),
|
||||||
.toLowerCase()
|
description: z.string().trim().optional(),
|
||||||
.trim()
|
permissions: z.any().array()
|
||||||
.min(1)
|
|
||||||
.refine(
|
|
||||||
(val) => !Object.values(ProjectMembershipRole).includes(val as ProjectMembershipRole),
|
|
||||||
"Please choose a different slug, the slug you have entered is reserved"
|
|
||||||
)
|
|
||||||
.refine((v) => slugify(v) === v, {
|
|
||||||
message: "Slug must be a valid"
|
|
||||||
})
|
|
||||||
.describe(PROJECT_ROLE.CREATE.slug),
|
|
||||||
name: z.string().min(1).trim().describe(PROJECT_ROLE.CREATE.name),
|
|
||||||
description: z.string().trim().optional().describe(PROJECT_ROLE.CREATE.description),
|
|
||||||
permissions: ProjectPermissionSchema.array().describe(PROJECT_ROLE.CREATE.permissions)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
role: SanitizedRoleSchema
|
role: ProjectRolesSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.projectRole.createRole({
|
const role = await server.services.projectRole.createRole(
|
||||||
actorAuthMethod: req.permission.authMethod,
|
req.permission.type,
|
||||||
actorId: req.permission.id,
|
req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
req.params.projectId,
|
||||||
actor: req.permission.type,
|
req.body,
|
||||||
projectSlug: req.params.projectSlug,
|
req.permission.authMethod,
|
||||||
data: {
|
req.permission.orgId
|
||||||
...req.body,
|
);
|
||||||
permissions: JSON.stringify(packRules(req.body.permissions))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return { role };
|
return { role };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
url: "/:projectSlug/roles/:roleId",
|
url: "/:projectId/roles/:roleId",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: writeLimit
|
rateLimit: writeLimit
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
description: "Update a project role",
|
|
||||||
security: [
|
|
||||||
{
|
|
||||||
bearerAuth: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectSlug: z.string().trim().describe(PROJECT_ROLE.UPDATE.projectSlug),
|
projectId: z.string().trim(),
|
||||||
roleId: z.string().trim().describe(PROJECT_ROLE.UPDATE.roleId)
|
roleId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
slug: z
|
slug: z.string().trim().optional(),
|
||||||
.string()
|
name: z.string().trim().optional(),
|
||||||
.toLowerCase()
|
description: z.string().trim().optional(),
|
||||||
.trim()
|
permissions: z.any().array()
|
||||||
.optional()
|
|
||||||
.describe(PROJECT_ROLE.UPDATE.slug)
|
|
||||||
.refine(
|
|
||||||
(val) =>
|
|
||||||
typeof val === "undefined" ||
|
|
||||||
!Object.values(ProjectMembershipRole).includes(val as ProjectMembershipRole),
|
|
||||||
"Please choose a different slug, the slug you have entered is reserved"
|
|
||||||
)
|
|
||||||
.refine((val) => typeof val === "undefined" || slugify(val) === val, {
|
|
||||||
message: "Slug must be a valid"
|
|
||||||
}),
|
|
||||||
name: z.string().trim().optional().describe(PROJECT_ROLE.UPDATE.name),
|
|
||||||
permissions: ProjectPermissionSchema.array().describe(PROJECT_ROLE.UPDATE.permissions)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
role: SanitizedRoleSchema
|
role: ProjectRolesSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.projectRole.updateRole({
|
const role = await server.services.projectRole.updateRole(
|
||||||
actorAuthMethod: req.permission.authMethod,
|
req.permission.type,
|
||||||
actorId: req.permission.id,
|
req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
req.params.projectId,
|
||||||
actor: req.permission.type,
|
req.params.roleId,
|
||||||
projectSlug: req.params.projectSlug,
|
req.body,
|
||||||
roleId: req.params.roleId,
|
req.permission.authMethod,
|
||||||
data: {
|
req.permission.orgId
|
||||||
...req.body,
|
);
|
||||||
permissions: JSON.stringify(packRules(req.body.permissions))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return { role };
|
return { role };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
url: "/:projectSlug/roles/:roleId",
|
url: "/:projectId/roles/:roleId",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: writeLimit
|
rateLimit: writeLimit
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
description: "Delete a project role",
|
|
||||||
security: [
|
|
||||||
{
|
|
||||||
bearerAuth: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectSlug: z.string().trim().describe(PROJECT_ROLE.DELETE.projectSlug),
|
projectId: z.string().trim(),
|
||||||
roleId: z.string().trim().describe(PROJECT_ROLE.DELETE.roleId)
|
roleId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
role: SanitizedRoleSchema
|
role: ProjectRolesSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.projectRole.deleteRole({
|
const role = await server.services.projectRole.deleteRole(
|
||||||
actorAuthMethod: req.permission.authMethod,
|
req.permission.type,
|
||||||
actorId: req.permission.id,
|
req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
req.params.projectId,
|
||||||
actor: req.permission.type,
|
req.params.roleId,
|
||||||
projectSlug: req.params.projectSlug,
|
req.permission.authMethod,
|
||||||
roleId: req.params.roleId
|
req.permission.orgId
|
||||||
});
|
);
|
||||||
return { role };
|
return { role };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:projectSlug/roles",
|
url: "/:projectId/roles",
|
||||||
config: {
|
|
||||||
rateLimit: readLimit
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
description: "List project role",
|
|
||||||
security: [
|
|
||||||
{
|
|
||||||
bearerAuth: []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
params: z.object({
|
|
||||||
projectSlug: z.string().trim().describe(PROJECT_ROLE.LIST.projectSlug)
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
roles: ProjectRolesSchema.omit({ permissions: true }).array()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const roles = await server.services.projectRole.listRoles({
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actor: req.permission.type,
|
|
||||||
projectSlug: req.params.projectSlug
|
|
||||||
});
|
|
||||||
return { roles };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
method: "GET",
|
|
||||||
url: "/:projectSlug/roles/slug/:slug",
|
|
||||||
config: {
|
config: {
|
||||||
rateLimit: readLimit
|
rateLimit: readLimit
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectSlug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.projectSlug),
|
projectId: z.string().trim()
|
||||||
slug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.roleSlug)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
role: SanitizedRoleSchema
|
data: z.object({
|
||||||
|
roles: ProjectRolesSchema.omit({ permissions: true })
|
||||||
|
.merge(z.object({ permissions: z.unknown() }))
|
||||||
|
.array()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.projectRole.getRoleBySlug({
|
const roles = await server.services.projectRole.listRoles(
|
||||||
actorAuthMethod: req.permission.authMethod,
|
req.permission.type,
|
||||||
actorId: req.permission.id,
|
req.permission.id,
|
||||||
actorOrgId: req.permission.orgId,
|
req.params.projectId,
|
||||||
actor: req.permission.type,
|
req.permission.authMethod,
|
||||||
projectSlug: req.params.projectSlug,
|
req.permission.orgId
|
||||||
roleSlug: req.params.slug
|
);
|
||||||
});
|
return { data: { roles } };
|
||||||
return { role };
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -102,12 +102,12 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
if (!profile) throw new BadRequestError({ message: "Missing profile" });
|
if (!profile) throw new BadRequestError({ message: "Missing profile" });
|
||||||
const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved
|
const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved
|
||||||
|
|
||||||
if (!email || !profile.firstName) {
|
if (!profile.email || !profile.firstName) {
|
||||||
throw new BadRequestError({ message: "Invalid request. Missing email or first name" });
|
throw new BadRequestError({ message: "Invalid request. Missing email or first name" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
|
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
|
||||||
externalId: profile.nameID,
|
username: profile.nameID ?? email,
|
||||||
email,
|
email,
|
||||||
firstName: profile.firstName as string,
|
firstName: profile.firstName as string,
|
||||||
lastName: profile.lastName as string,
|
lastName: profile.lastName as string,
|
||||||
|
@ -153,7 +153,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const users = await req.server.services.scim.listScimUsers({
|
const users = await req.server.services.scim.listScimUsers({
|
||||||
startIndex: req.query.startIndex,
|
offset: req.query.startIndex,
|
||||||
limit: req.query.count,
|
limit: req.query.count,
|
||||||
filter: req.query.filter,
|
filter: req.query.filter,
|
||||||
orgId: req.permission.orgId
|
orgId: req.permission.orgId
|
||||||
@ -163,11 +163,11 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/Users/:orgMembershipId",
|
url: "/Users/:userId",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
orgMembershipId: z.string().trim()
|
userId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
201: z.object({
|
201: z.object({
|
||||||
@ -193,7 +193,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const user = await req.server.services.scim.getScimUser({
|
const user = await req.server.services.scim.getScimUser({
|
||||||
orgMembershipId: req.params.orgMembershipId,
|
userId: req.params.userId,
|
||||||
orgId: req.permission.orgId
|
orgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
return user;
|
return user;
|
||||||
@ -249,7 +249,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
const primaryEmail = req.body.emails?.find((email) => email.primary)?.value;
|
const primaryEmail = req.body.emails?.find((email) => email.primary)?.value;
|
||||||
|
|
||||||
const user = await req.server.services.scim.createScimUser({
|
const user = await req.server.services.scim.createScimUser({
|
||||||
externalId: req.body.userName,
|
username: req.body.userName,
|
||||||
email: primaryEmail,
|
email: primaryEmail,
|
||||||
firstName: req.body.name.givenName,
|
firstName: req.body.name.givenName,
|
||||||
lastName: req.body.name.familyName,
|
lastName: req.body.name.familyName,
|
||||||
@ -261,11 +261,11 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/Users/:orgMembershipId",
|
url: "/Users/:userId",
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
orgMembershipId: z.string().trim()
|
userId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({})
|
200: z.object({})
|
||||||
@ -274,7 +274,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const user = await req.server.services.scim.deleteScimUser({
|
const user = await req.server.services.scim.deleteScimUser({
|
||||||
orgMembershipId: req.params.orgMembershipId,
|
userId: req.params.userId,
|
||||||
orgId: req.permission.orgId
|
orgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -361,7 +361,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const groups = await req.server.services.scim.listScimGroups({
|
const groups = await req.server.services.scim.listScimGroups({
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
startIndex: req.query.startIndex,
|
offset: req.query.startIndex,
|
||||||
limit: req.query.count
|
limit: req.query.count
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -416,10 +416,10 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
displayName: z.string().trim(),
|
displayName: z.string().trim(),
|
||||||
members: z.array(
|
members: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
value: z.string(), // infisical orgMembershipId
|
value: z.string(), // infisical userId
|
||||||
display: z.string()
|
display: z.string()
|
||||||
})
|
})
|
||||||
)
|
) // note: is this where members are added to group?
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -534,11 +534,11 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/Users/:orgMembershipId",
|
url: "/Users/:userId",
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
orgMembershipId: z.string().trim()
|
userId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
schemas: z.array(z.string()),
|
schemas: z.array(z.string()),
|
||||||
@ -575,7 +575,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const user = await req.server.services.scim.replaceScimUser({
|
const user = await req.server.services.scim.replaceScimUser({
|
||||||
orgMembershipId: req.params.orgMembershipId,
|
userId: req.params.userId,
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
active: req.body.active
|
active: req.body.active
|
||||||
});
|
});
|
||||||
|
@ -32,20 +32,22 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
approvals: SecretApprovalRequestsSchema.extend({
|
approvals: SecretApprovalRequestsSchema.merge(
|
||||||
// secretPath: z.string(),
|
z.object({
|
||||||
policy: z.object({
|
// secretPath: z.string(),
|
||||||
id: z.string(),
|
policy: z.object({
|
||||||
name: z.string(),
|
id: z.string(),
|
||||||
approvals: z.number(),
|
name: z.string(),
|
||||||
approvers: z.string().array(),
|
approvals: z.number(),
|
||||||
secretPath: z.string().optional().nullable()
|
approvers: z.string().array(),
|
||||||
}),
|
secretPath: z.string().optional().nullable()
|
||||||
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
|
}),
|
||||||
environment: z.string(),
|
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
|
||||||
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
|
environment: z.string(),
|
||||||
approvers: z.string().array()
|
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
|
||||||
}).array()
|
approvers: z.string().array()
|
||||||
|
})
|
||||||
|
).array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
import { TDbClient } from "@app/db";
|
|
||||||
import { TableName } from "@app/db/schemas";
|
|
||||||
import { ormify } from "@app/lib/knex";
|
|
||||||
|
|
||||||
export type TAccessApprovalPolicyApproverDALFactory = ReturnType<typeof accessApprovalPolicyApproverDALFactory>;
|
|
||||||
|
|
||||||
export const accessApprovalPolicyApproverDALFactory = (db: TDbClient) => {
|
|
||||||
const accessApprovalPolicyApproverOrm = ormify(db, TableName.AccessApprovalPolicyApprover);
|
|
||||||
return { ...accessApprovalPolicyApproverOrm };
|
|
||||||
};
|
|
@ -1,76 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
|
||||||
import { TableName, TAccessApprovalPolicies } from "@app/db/schemas";
|
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
|
||||||
import { buildFindFilter, mergeOneToManyRelation, ormify, selectAllTableCols, TFindFilter } from "@app/lib/knex";
|
|
||||||
|
|
||||||
export type TAccessApprovalPolicyDALFactory = ReturnType<typeof accessApprovalPolicyDALFactory>;
|
|
||||||
|
|
||||||
export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
|
||||||
const accessApprovalPolicyOrm = ormify(db, TableName.AccessApprovalPolicy);
|
|
||||||
|
|
||||||
const accessApprovalPolicyFindQuery = async (tx: Knex, filter: TFindFilter<TAccessApprovalPolicies>) => {
|
|
||||||
const result = await tx(TableName.AccessApprovalPolicy)
|
|
||||||
// eslint-disable-next-line
|
|
||||||
.where(buildFindFilter(filter))
|
|
||||||
.join(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
|
||||||
.join(
|
|
||||||
TableName.AccessApprovalPolicyApprover,
|
|
||||||
`${TableName.AccessApprovalPolicy}.id`,
|
|
||||||
`${TableName.AccessApprovalPolicyApprover}.policyId`
|
|
||||||
)
|
|
||||||
.select(tx.ref("approverId").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"))
|
|
||||||
.select(tx.ref("projectId").withSchema(TableName.Environment))
|
|
||||||
.select(selectAllTableCols(TableName.AccessApprovalPolicy));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
|
||||||
try {
|
|
||||||
const doc = await accessApprovalPolicyFindQuery(tx || db, {
|
|
||||||
[`${TableName.AccessApprovalPolicy}.id` as "id"]: id
|
|
||||||
});
|
|
||||||
const formatedDoc = mergeOneToManyRelation(
|
|
||||||
doc,
|
|
||||||
"id",
|
|
||||||
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
|
||||||
...el,
|
|
||||||
envId,
|
|
||||||
environment: { id: envId, name, slug }
|
|
||||||
}),
|
|
||||||
({ approverId }) => approverId,
|
|
||||||
"approvers"
|
|
||||||
);
|
|
||||||
return formatedDoc?.[0];
|
|
||||||
} catch (error) {
|
|
||||||
throw new DatabaseError({ error, name: "FindById" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const find = async (filter: TFindFilter<TAccessApprovalPolicies & { projectId: string }>, tx?: Knex) => {
|
|
||||||
try {
|
|
||||||
const docs = await accessApprovalPolicyFindQuery(tx || db, filter);
|
|
||||||
const formatedDoc = mergeOneToManyRelation(
|
|
||||||
docs,
|
|
||||||
"id",
|
|
||||||
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
|
||||||
...el,
|
|
||||||
envId,
|
|
||||||
environment: { id: envId, name, slug }
|
|
||||||
}),
|
|
||||||
({ approverId }) => approverId,
|
|
||||||
"approvers"
|
|
||||||
);
|
|
||||||
return formatedDoc.map((policy) => ({ ...policy, secretPath: policy.secretPath || undefined }));
|
|
||||||
} catch (error) {
|
|
||||||
throw new DatabaseError({ error, name: "Find" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return { ...accessApprovalPolicyOrm, find, findById };
|
|
||||||
};
|
|
@ -1,36 +0,0 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
|
||||||
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
|
||||||
|
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
|
|
||||||
import { TVerifyApprovers } from "./access-approval-policy-types";
|
|
||||||
|
|
||||||
export const verifyApprovers = async ({
|
|
||||||
userIds,
|
|
||||||
projectId,
|
|
||||||
orgId,
|
|
||||||
envSlug,
|
|
||||||
actorAuthMethod,
|
|
||||||
secretPath,
|
|
||||||
permissionService
|
|
||||||
}: TVerifyApprovers) => {
|
|
||||||
for await (const userId of userIds) {
|
|
||||||
try {
|
|
||||||
const { permission: approverPermission } = await permissionService.getProjectPermission(
|
|
||||||
ActorType.USER,
|
|
||||||
userId,
|
|
||||||
projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
orgId
|
|
||||||
);
|
|
||||||
|
|
||||||
ForbiddenError.from(approverPermission).throwUnlessCan(
|
|
||||||
ProjectPermissionActions.Create,
|
|
||||||
subject(ProjectPermissionSub.Secrets, { environment: envSlug, secretPath })
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
throw new BadRequestError({ message: "One or more approvers doesn't have access to be specified secret path" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,273 +0,0 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
|
||||||
|
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
|
||||||
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 { TAccessApprovalPolicyApproverDALFactory } from "./access-approval-policy-approver-dal";
|
|
||||||
import { TAccessApprovalPolicyDALFactory } from "./access-approval-policy-dal";
|
|
||||||
import { verifyApprovers } from "./access-approval-policy-fns";
|
|
||||||
import {
|
|
||||||
TCreateAccessApprovalPolicy,
|
|
||||||
TDeleteAccessApprovalPolicy,
|
|
||||||
TGetAccessPolicyCountByEnvironmentDTO,
|
|
||||||
TListAccessApprovalPoliciesDTO,
|
|
||||||
TUpdateAccessApprovalPolicy
|
|
||||||
} from "./access-approval-policy-types";
|
|
||||||
|
|
||||||
type TSecretApprovalPolicyServiceFactoryDep = {
|
|
||||||
projectDAL: TProjectDALFactory;
|
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
|
||||||
accessApprovalPolicyDAL: TAccessApprovalPolicyDALFactory;
|
|
||||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findOne">;
|
|
||||||
accessApprovalPolicyApproverDAL: TAccessApprovalPolicyApproverDALFactory;
|
|
||||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TAccessApprovalPolicyServiceFactory = ReturnType<typeof accessApprovalPolicyServiceFactory>;
|
|
||||||
|
|
||||||
export const accessApprovalPolicyServiceFactory = ({
|
|
||||||
accessApprovalPolicyDAL,
|
|
||||||
accessApprovalPolicyApproverDAL,
|
|
||||||
permissionService,
|
|
||||||
projectEnvDAL,
|
|
||||||
projectDAL,
|
|
||||||
projectMembershipDAL
|
|
||||||
}: TSecretApprovalPolicyServiceFactoryDep) => {
|
|
||||||
const createAccessApprovalPolicy = async ({
|
|
||||||
name,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorOrgId,
|
|
||||||
secretPath,
|
|
||||||
actorAuthMethod,
|
|
||||||
approvals,
|
|
||||||
approvers,
|
|
||||||
projectSlug,
|
|
||||||
environment
|
|
||||||
}: TCreateAccessApprovalPolicy) => {
|
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
|
||||||
if (!project) throw new BadRequestError({ message: "Project not found" });
|
|
||||||
|
|
||||||
if (approvals > approvers.length)
|
|
||||||
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
project.id,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
ProjectPermissionActions.Create,
|
|
||||||
ProjectPermissionSub.SecretApproval
|
|
||||||
);
|
|
||||||
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 }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (secretApprovers.length !== approvers.length) {
|
|
||||||
throw new BadRequestError({ message: "Approver not found in project" });
|
|
||||||
}
|
|
||||||
|
|
||||||
await verifyApprovers({
|
|
||||||
projectId: project.id,
|
|
||||||
orgId: actorOrgId,
|
|
||||||
envSlug: environment,
|
|
||||||
secretPath,
|
|
||||||
actorAuthMethod,
|
|
||||||
permissionService,
|
|
||||||
userIds: secretApprovers.map((approver) => approver.userId)
|
|
||||||
});
|
|
||||||
|
|
||||||
const accessApproval = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
|
||||||
const doc = await accessApprovalPolicyDAL.create(
|
|
||||||
{
|
|
||||||
envId: env.id,
|
|
||||||
approvals,
|
|
||||||
secretPath,
|
|
||||||
name
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
await accessApprovalPolicyApproverDAL.insertMany(
|
|
||||||
secretApprovers.map(({ id }) => ({
|
|
||||||
approverId: id,
|
|
||||||
policyId: doc.id
|
|
||||||
})),
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
return doc;
|
|
||||||
});
|
|
||||||
return { ...accessApproval, environment: env, projectId: project.id };
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAccessApprovalPolicyByProjectSlug = async ({
|
|
||||||
actorId,
|
|
||||||
actor,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod,
|
|
||||||
projectSlug
|
|
||||||
}: TListAccessApprovalPoliciesDTO) => {
|
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
|
||||||
if (!project) throw new BadRequestError({ message: "Project not found" });
|
|
||||||
|
|
||||||
// Anyone in the project should be able to get the policies.
|
|
||||||
/* const { permission } = */ await permissionService.getProjectPermission(
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
project.id,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
// ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
|
||||||
|
|
||||||
const accessApprovalPolicies = await accessApprovalPolicyDAL.find({ projectId: project.id });
|
|
||||||
return accessApprovalPolicies;
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateAccessApprovalPolicy = async ({
|
|
||||||
policyId,
|
|
||||||
approvers,
|
|
||||||
secretPath,
|
|
||||||
name,
|
|
||||||
actorId,
|
|
||||||
actor,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod,
|
|
||||||
approvals
|
|
||||||
}: TUpdateAccessApprovalPolicy) => {
|
|
||||||
const accessApprovalPolicy = await accessApprovalPolicyDAL.findById(policyId);
|
|
||||||
if (!accessApprovalPolicy) throw new BadRequestError({ message: "Secret approval policy not found" });
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
accessApprovalPolicy.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
|
||||||
|
|
||||||
const updatedPolicy = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
|
||||||
const doc = await accessApprovalPolicyDAL.updateById(
|
|
||||||
accessApprovalPolicy.id,
|
|
||||||
{
|
|
||||||
approvals,
|
|
||||||
secretPath,
|
|
||||||
name
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
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 }
|
|
||||||
);
|
|
||||||
|
|
||||||
await verifyApprovers({
|
|
||||||
projectId: accessApprovalPolicy.projectId,
|
|
||||||
orgId: actorOrgId,
|
|
||||||
envSlug: accessApprovalPolicy.environment.slug,
|
|
||||||
secretPath: doc.secretPath!,
|
|
||||||
actorAuthMethod,
|
|
||||||
permissionService,
|
|
||||||
userIds: secretApprovers.map((approver) => approver.userId)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (secretApprovers.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,
|
|
||||||
policyId: doc.id
|
|
||||||
})),
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return doc;
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
...updatedPolicy,
|
|
||||||
environment: accessApprovalPolicy.environment,
|
|
||||||
projectId: accessApprovalPolicy.projectId
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteAccessApprovalPolicy = async ({
|
|
||||||
policyId,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
}: TDeleteAccessApprovalPolicy) => {
|
|
||||||
const policy = await accessApprovalPolicyDAL.findById(policyId);
|
|
||||||
if (!policy) throw new BadRequestError({ message: "Secret approval policy not found" });
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
policy.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
ProjectPermissionActions.Delete,
|
|
||||||
ProjectPermissionSub.SecretApproval
|
|
||||||
);
|
|
||||||
|
|
||||||
await accessApprovalPolicyDAL.deleteById(policyId);
|
|
||||||
return policy;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getAccessPolicyCountByEnvSlug = async ({
|
|
||||||
actor,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod,
|
|
||||||
projectSlug,
|
|
||||||
actorId,
|
|
||||||
envSlug
|
|
||||||
}: TGetAccessPolicyCountByEnvironmentDTO) => {
|
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
|
||||||
|
|
||||||
if (!project) throw new BadRequestError({ message: "Project not found" });
|
|
||||||
|
|
||||||
const { membership } = await permissionService.getProjectPermission(
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
project.id,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
if (!membership) throw new BadRequestError({ message: "User not found in project" });
|
|
||||||
|
|
||||||
const environment = await projectEnvDAL.findOne({ projectId: project.id, slug: envSlug });
|
|
||||||
if (!environment) throw new BadRequestError({ message: "Environment not found" });
|
|
||||||
|
|
||||||
const policies = await accessApprovalPolicyDAL.find({ envId: environment.id, projectId: project.id });
|
|
||||||
if (!policies) throw new BadRequestError({ message: "No policies found" });
|
|
||||||
|
|
||||||
return { count: policies.length };
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
getAccessPolicyCountByEnvSlug,
|
|
||||||
createAccessApprovalPolicy,
|
|
||||||
deleteAccessApprovalPolicy,
|
|
||||||
updateAccessApprovalPolicy,
|
|
||||||
getAccessApprovalPolicyByProjectSlug
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,44 +0,0 @@
|
|||||||
import { TProjectPermission } from "@app/lib/types";
|
|
||||||
import { ActorAuthMethod } from "@app/services/auth/auth-type";
|
|
||||||
|
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
|
||||||
|
|
||||||
export type TVerifyApprovers = {
|
|
||||||
userIds: string[];
|
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
|
||||||
envSlug: string;
|
|
||||||
actorAuthMethod: ActorAuthMethod;
|
|
||||||
secretPath: string;
|
|
||||||
projectId: string;
|
|
||||||
orgId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TCreateAccessApprovalPolicy = {
|
|
||||||
approvals: number;
|
|
||||||
secretPath: string;
|
|
||||||
environment: string;
|
|
||||||
approvers: string[];
|
|
||||||
projectSlug: string;
|
|
||||||
name: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TUpdateAccessApprovalPolicy = {
|
|
||||||
policyId: string;
|
|
||||||
approvals?: number;
|
|
||||||
approvers?: string[];
|
|
||||||
secretPath?: string;
|
|
||||||
name?: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TDeleteAccessApprovalPolicy = {
|
|
||||||
policyId: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TGetAccessPolicyCountByEnvironmentDTO = {
|
|
||||||
envSlug: string;
|
|
||||||
projectSlug: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TListAccessApprovalPoliciesDTO = {
|
|
||||||
projectSlug: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
@ -1,266 +0,0 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
|
||||||
import { AccessApprovalRequestsSchema, TableName, TAccessApprovalRequests } from "@app/db/schemas";
|
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
|
||||||
import { ormify, selectAllTableCols, sqlNestRelationships, TFindFilter } from "@app/lib/knex";
|
|
||||||
|
|
||||||
import { ApprovalStatus } from "./access-approval-request-types";
|
|
||||||
|
|
||||||
export type TAccessApprovalRequestDALFactory = ReturnType<typeof accessApprovalRequestDALFactory>;
|
|
||||||
|
|
||||||
export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|
||||||
const accessApprovalRequestOrm = ormify(db, TableName.AccessApprovalRequest);
|
|
||||||
|
|
||||||
const findRequestsWithPrivilegeByPolicyIds = async (policyIds: string[]) => {
|
|
||||||
try {
|
|
||||||
const docs = await db(TableName.AccessApprovalRequest)
|
|
||||||
.whereIn(`${TableName.AccessApprovalRequest}.policyId`, policyIds)
|
|
||||||
|
|
||||||
.leftJoin(
|
|
||||||
TableName.ProjectUserAdditionalPrivilege,
|
|
||||||
`${TableName.AccessApprovalRequest}.privilegeId`,
|
|
||||||
`${TableName.ProjectUserAdditionalPrivilege}.id`
|
|
||||||
)
|
|
||||||
.leftJoin(
|
|
||||||
TableName.AccessApprovalPolicy,
|
|
||||||
`${TableName.AccessApprovalRequest}.policyId`,
|
|
||||||
`${TableName.AccessApprovalPolicy}.id`
|
|
||||||
)
|
|
||||||
|
|
||||||
.leftJoin(
|
|
||||||
TableName.AccessApprovalRequestReviewer,
|
|
||||||
`${TableName.AccessApprovalRequest}.id`,
|
|
||||||
`${TableName.AccessApprovalRequestReviewer}.requestId`
|
|
||||||
)
|
|
||||||
.leftJoin(
|
|
||||||
TableName.AccessApprovalPolicyApprover,
|
|
||||||
`${TableName.AccessApprovalPolicy}.id`,
|
|
||||||
`${TableName.AccessApprovalPolicyApprover}.policyId`
|
|
||||||
)
|
|
||||||
|
|
||||||
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
|
||||||
|
|
||||||
.select(selectAllTableCols(TableName.AccessApprovalRequest))
|
|
||||||
.select(
|
|
||||||
db.ref("id").withSchema(TableName.AccessApprovalPolicy).as("policyId"),
|
|
||||||
db.ref("name").withSchema(TableName.AccessApprovalPolicy).as("policyName"),
|
|
||||||
db.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals"),
|
|
||||||
db.ref("secretPath").withSchema(TableName.AccessApprovalPolicy).as("policySecretPath"),
|
|
||||||
db.ref("envId").withSchema(TableName.AccessApprovalPolicy).as("policyEnvId")
|
|
||||||
)
|
|
||||||
|
|
||||||
.select(db.ref("approverId").withSchema(TableName.AccessApprovalPolicyApprover))
|
|
||||||
|
|
||||||
.select(
|
|
||||||
db.ref("projectId").withSchema(TableName.Environment),
|
|
||||||
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
|
||||||
db.ref("name").withSchema(TableName.Environment).as("envName")
|
|
||||||
)
|
|
||||||
|
|
||||||
.select(
|
|
||||||
db.ref("member").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerMemberId"),
|
|
||||||
db.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus")
|
|
||||||
)
|
|
||||||
|
|
||||||
.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"),
|
|
||||||
db
|
|
||||||
.ref("temporaryAccessStartTime")
|
|
||||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
|
||||||
.as("privilegeTemporaryAccessStartTime"),
|
|
||||||
db
|
|
||||||
.ref("temporaryAccessEndTime")
|
|
||||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
|
||||||
.as("privilegeTemporaryAccessEndTime"),
|
|
||||||
|
|
||||||
db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegePermissions")
|
|
||||||
)
|
|
||||||
.orderBy(`${TableName.AccessApprovalRequest}.createdAt`, "desc");
|
|
||||||
|
|
||||||
const formattedDocs = sqlNestRelationships({
|
|
||||||
data: docs,
|
|
||||||
key: "id",
|
|
||||||
parentMapper: (doc) => ({
|
|
||||||
...AccessApprovalRequestsSchema.parse(doc),
|
|
||||||
projectId: doc.projectId,
|
|
||||||
environment: doc.envSlug,
|
|
||||||
environmentName: doc.envName,
|
|
||||||
policy: {
|
|
||||||
id: doc.policyId,
|
|
||||||
name: doc.policyName,
|
|
||||||
approvals: doc.policyApprovals,
|
|
||||||
secretPath: doc.policySecretPath,
|
|
||||||
envId: doc.policyEnvId
|
|
||||||
},
|
|
||||||
privilege: doc.privilegeId
|
|
||||||
? {
|
|
||||||
membershipId: doc.privilegeMembershipId,
|
|
||||||
isTemporary: doc.privilegeIsTemporary,
|
|
||||||
temporaryMode: doc.privilegeTemporaryMode,
|
|
||||||
temporaryRange: doc.privilegeTemporaryRange,
|
|
||||||
temporaryAccessStartTime: doc.privilegeTemporaryAccessStartTime,
|
|
||||||
temporaryAccessEndTime: doc.privilegeTemporaryAccessEndTime,
|
|
||||||
permissions: doc.privilegePermissions
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
|
|
||||||
isApproved: !!doc.privilegeId
|
|
||||||
}),
|
|
||||||
childrenMapper: [
|
|
||||||
{
|
|
||||||
key: "reviewerMemberId",
|
|
||||||
label: "reviewers" as const,
|
|
||||||
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
|
|
||||||
},
|
|
||||||
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!formattedDocs) return [];
|
|
||||||
|
|
||||||
return formattedDocs.map((doc) => ({
|
|
||||||
...doc,
|
|
||||||
policy: { ...doc.policy, approvers: doc.approvers }
|
|
||||||
}));
|
|
||||||
} catch (error) {
|
|
||||||
throw new DatabaseError({ error, name: "FindRequestsWithPrivilege" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const findQuery = (filter: TFindFilter<TAccessApprovalRequests>, tx: Knex) =>
|
|
||||||
tx(TableName.AccessApprovalRequest)
|
|
||||||
.where(filter)
|
|
||||||
.join(
|
|
||||||
TableName.AccessApprovalPolicy,
|
|
||||||
`${TableName.AccessApprovalRequest}.policyId`,
|
|
||||||
`${TableName.AccessApprovalPolicy}.id`
|
|
||||||
)
|
|
||||||
|
|
||||||
.join(
|
|
||||||
TableName.AccessApprovalPolicyApprover,
|
|
||||||
`${TableName.AccessApprovalPolicy}.id`,
|
|
||||||
`${TableName.AccessApprovalPolicyApprover}.policyId`
|
|
||||||
)
|
|
||||||
.leftJoin(
|
|
||||||
TableName.AccessApprovalRequestReviewer,
|
|
||||||
`${TableName.AccessApprovalRequest}.id`,
|
|
||||||
`${TableName.AccessApprovalRequestReviewer}.requestId`
|
|
||||||
)
|
|
||||||
|
|
||||||
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
|
||||||
.select(selectAllTableCols(TableName.AccessApprovalRequest))
|
|
||||||
.select(
|
|
||||||
tx.ref("member").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerMemberId"),
|
|
||||||
tx.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus"),
|
|
||||||
tx.ref("id").withSchema(TableName.AccessApprovalPolicy).as("policyId"),
|
|
||||||
tx.ref("name").withSchema(TableName.AccessApprovalPolicy).as("policyName"),
|
|
||||||
tx.ref("projectId").withSchema(TableName.Environment),
|
|
||||||
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)
|
|
||||||
);
|
|
||||||
|
|
||||||
const findById = async (id: string, tx?: Knex) => {
|
|
||||||
try {
|
|
||||||
const sql = findQuery({ [`${TableName.AccessApprovalRequest}.id` as "id"]: id }, tx || db);
|
|
||||||
const docs = await sql;
|
|
||||||
const formatedDoc = sqlNestRelationships({
|
|
||||||
data: docs,
|
|
||||||
key: "id",
|
|
||||||
parentMapper: (el) => ({
|
|
||||||
...AccessApprovalRequestsSchema.parse(el),
|
|
||||||
projectId: el.projectId,
|
|
||||||
environment: el.environment,
|
|
||||||
policy: {
|
|
||||||
id: el.policyId,
|
|
||||||
name: el.policyName,
|
|
||||||
approvals: el.policyApprovals,
|
|
||||||
secretPath: el.policySecretPath
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
childrenMapper: [
|
|
||||||
{
|
|
||||||
key: "reviewerMemberId",
|
|
||||||
label: "reviewers" as const,
|
|
||||||
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
|
|
||||||
},
|
|
||||||
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
if (!formatedDoc?.[0]) return;
|
|
||||||
return {
|
|
||||||
...formatedDoc[0],
|
|
||||||
policy: { ...formatedDoc[0].policy, approvers: formatedDoc[0].approvers }
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
throw new DatabaseError({ error, name: "FindByIdAccessApprovalRequest" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCount = async ({ projectId }: { projectId: string }) => {
|
|
||||||
try {
|
|
||||||
const accessRequests = await db(TableName.AccessApprovalRequest)
|
|
||||||
.leftJoin(
|
|
||||||
TableName.AccessApprovalPolicy,
|
|
||||||
`${TableName.AccessApprovalRequest}.policyId`,
|
|
||||||
`${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`,
|
|
||||||
`${TableName.AccessApprovalRequestReviewer}.requestId`
|
|
||||||
)
|
|
||||||
|
|
||||||
.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"));
|
|
||||||
|
|
||||||
const formattedRequests = sqlNestRelationships({
|
|
||||||
data: accessRequests,
|
|
||||||
key: "id",
|
|
||||||
parentMapper: (doc) => ({
|
|
||||||
...AccessApprovalRequestsSchema.parse(doc)
|
|
||||||
}),
|
|
||||||
childrenMapper: [
|
|
||||||
{
|
|
||||||
key: "reviewerMemberId",
|
|
||||||
label: "reviewers" as const,
|
|
||||||
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, 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)
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
);
|
|
||||||
|
|
||||||
return { pendingCount: pendingApprovals.length, finalizedCount: finalizedApprovals.length };
|
|
||||||
} catch (error) {
|
|
||||||
throw new DatabaseError({ error, name: "GetCountAccessApprovalRequest" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return { ...accessApprovalRequestOrm, findById, findRequestsWithPrivilegeByPolicyIds, getCount };
|
|
||||||
};
|
|
@ -1,53 +0,0 @@
|
|||||||
import { PackRule, unpackRules } from "@casl/ability/extra";
|
|
||||||
|
|
||||||
import { UnauthorizedError } from "@app/lib/errors";
|
|
||||||
|
|
||||||
import { TVerifyPermission } from "./access-approval-request-types";
|
|
||||||
|
|
||||||
function filterUnique(value: string, index: number, array: string[]) {
|
|
||||||
return array.indexOf(value) === index;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const verifyRequestedPermissions = ({ permissions }: TVerifyPermission) => {
|
|
||||||
const permission = unpackRules(
|
|
||||||
permissions as PackRule<{
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
conditions?: Record<string, any>;
|
|
||||||
action: string;
|
|
||||||
subject: [string];
|
|
||||||
}>[]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!permission || !permission.length) {
|
|
||||||
throw new UnauthorizedError({ message: "No permission provided" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestedPermissions: string[] = [];
|
|
||||||
|
|
||||||
for (const p of permission) {
|
|
||||||
if (p.action[0] === "read") requestedPermissions.push("Read Access");
|
|
||||||
if (p.action[0] === "create") requestedPermissions.push("Create Access");
|
|
||||||
if (p.action[0] === "delete") requestedPermissions.push("Delete Access");
|
|
||||||
if (p.action[0] === "edit") requestedPermissions.push("Edit Access");
|
|
||||||
}
|
|
||||||
|
|
||||||
const firstPermission = permission[0];
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
|
||||||
const permissionSecretPath = firstPermission.conditions?.secretPath?.$glob;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-unsafe-assignment
|
|
||||||
const permissionEnv = firstPermission.conditions?.environment;
|
|
||||||
|
|
||||||
if (!permissionEnv || typeof permissionEnv !== "string") {
|
|
||||||
throw new UnauthorizedError({ message: "Permission environment is not a string" });
|
|
||||||
}
|
|
||||||
if (!permissionSecretPath || typeof permissionSecretPath !== "string") {
|
|
||||||
throw new UnauthorizedError({ message: "Permission path is not a string" });
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
envSlug: permissionEnv,
|
|
||||||
secretPath: permissionSecretPath,
|
|
||||||
accessTypes: requestedPermissions.filter(filterUnique)
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,10 +0,0 @@
|
|||||||
import { TDbClient } from "@app/db";
|
|
||||||
import { TableName } from "@app/db/schemas";
|
|
||||||
import { ormify } from "@app/lib/knex";
|
|
||||||
|
|
||||||
export type TAccessApprovalRequestReviewerDALFactory = ReturnType<typeof accessApprovalRequestReviewerDALFactory>;
|
|
||||||
|
|
||||||
export const accessApprovalRequestReviewerDALFactory = (db: TDbClient) => {
|
|
||||||
const secretApprovalRequestReviewerOrm = ormify(db, TableName.AccessApprovalRequestReviewer);
|
|
||||||
return secretApprovalRequestReviewerOrm;
|
|
||||||
};
|
|
@ -1,369 +0,0 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { ProjectMembershipRole } from "@app/db/schemas";
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
|
||||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
|
||||||
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 { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
|
||||||
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 { TPermissionServiceFactory } from "../permission/permission-service";
|
|
||||||
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";
|
|
||||||
import { verifyRequestedPermissions } from "./access-approval-request-fns";
|
|
||||||
import { TAccessApprovalRequestReviewerDALFactory } from "./access-approval-request-reviewer-dal";
|
|
||||||
import {
|
|
||||||
ApprovalStatus,
|
|
||||||
TCreateAccessApprovalRequestDTO,
|
|
||||||
TGetAccessRequestCountDTO,
|
|
||||||
TListApprovalRequestsDTO,
|
|
||||||
TReviewAccessRequestDTO
|
|
||||||
} from "./access-approval-request-types";
|
|
||||||
|
|
||||||
type TSecretApprovalRequestServiceFactoryDep = {
|
|
||||||
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "create" | "findById">;
|
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
|
||||||
accessApprovalPolicyApproverDAL: Pick<TAccessApprovalPolicyApproverDALFactory, "find">;
|
|
||||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
|
||||||
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus" | "findProjectBySlug">;
|
|
||||||
accessApprovalRequestDAL: Pick<
|
|
||||||
TAccessApprovalRequestDALFactory,
|
|
||||||
| "create"
|
|
||||||
| "find"
|
|
||||||
| "findRequestsWithPrivilegeByPolicyIds"
|
|
||||||
| "findById"
|
|
||||||
| "transaction"
|
|
||||||
| "updateById"
|
|
||||||
| "findOne"
|
|
||||||
| "getCount"
|
|
||||||
>;
|
|
||||||
accessApprovalPolicyDAL: Pick<TAccessApprovalPolicyDALFactory, "findOne" | "find">;
|
|
||||||
accessApprovalRequestReviewerDAL: Pick<
|
|
||||||
TAccessApprovalRequestReviewerDALFactory,
|
|
||||||
"create" | "find" | "findOne" | "transaction"
|
|
||||||
>;
|
|
||||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findById">;
|
|
||||||
smtpService: Pick<TSmtpService, "sendMail">;
|
|
||||||
userDAL: Pick<TUserDALFactory, "findUserByProjectMembershipId" | "findUsersByProjectMembershipIds">;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TAccessApprovalRequestServiceFactory = ReturnType<typeof accessApprovalRequestServiceFactory>;
|
|
||||||
|
|
||||||
export const accessApprovalRequestServiceFactory = ({
|
|
||||||
projectDAL,
|
|
||||||
projectEnvDAL,
|
|
||||||
permissionService,
|
|
||||||
accessApprovalRequestDAL,
|
|
||||||
accessApprovalRequestReviewerDAL,
|
|
||||||
projectMembershipDAL,
|
|
||||||
accessApprovalPolicyDAL,
|
|
||||||
accessApprovalPolicyApproverDAL,
|
|
||||||
additionalPrivilegeDAL,
|
|
||||||
smtpService,
|
|
||||||
userDAL
|
|
||||||
}: TSecretApprovalRequestServiceFactoryDep) => {
|
|
||||||
const createAccessApprovalRequest = async ({
|
|
||||||
isTemporary,
|
|
||||||
temporaryRange,
|
|
||||||
actorId,
|
|
||||||
permissions: requestedPermissions,
|
|
||||||
actor,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod,
|
|
||||||
projectSlug
|
|
||||||
}: TCreateAccessApprovalRequestDTO) => {
|
|
||||||
const cfg = getConfig();
|
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
|
||||||
if (!project) throw new UnauthorizedError({ message: "Project not found" });
|
|
||||||
|
|
||||||
// Anyone can create an access approval request.
|
|
||||||
const { membership } = await permissionService.getProjectPermission(
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
project.id,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
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 });
|
|
||||||
const environment = await projectEnvDAL.findOne({ projectId: project.id, slug: envSlug });
|
|
||||||
|
|
||||||
if (!environment) throw new UnauthorizedError({ message: "Environment not found" });
|
|
||||||
|
|
||||||
const policy = await accessApprovalPolicyDAL.findOne({
|
|
||||||
envId: environment.id,
|
|
||||||
secretPath
|
|
||||||
});
|
|
||||||
if (!policy) throw new UnauthorizedError({ message: "No policy matching criteria was found." });
|
|
||||||
|
|
||||||
const approvers = await accessApprovalPolicyApproverDAL.find({
|
|
||||||
policyId: policy.id
|
|
||||||
});
|
|
||||||
|
|
||||||
const approverUsers = await userDAL.findUsersByProjectMembershipIds(
|
|
||||||
approvers.map((approver) => approver.approverId)
|
|
||||||
);
|
|
||||||
|
|
||||||
const duplicateRequests = await accessApprovalRequestDAL.find({
|
|
||||||
policyId: policy.id,
|
|
||||||
requestedBy: membership.id,
|
|
||||||
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);
|
|
||||||
|
|
||||||
const isExpired = new Date() > new Date(privilege.temporaryAccessEndTime || ("" as string));
|
|
||||||
|
|
||||||
if (!isExpired || !privilege.isTemporary) {
|
|
||||||
throw new BadRequestError({ message: "You already have an active privilege with the same criteria" });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const reviewers = await accessApprovalRequestReviewerDAL.find({
|
|
||||||
requestId: duplicateRequest.id
|
|
||||||
});
|
|
||||||
|
|
||||||
const isRejected = reviewers.some((reviewer) => reviewer.status === ApprovalStatus.REJECTED);
|
|
||||||
|
|
||||||
if (!isRejected) {
|
|
||||||
throw new BadRequestError({ message: "You already have a pending access request with the same criteria" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const approval = await accessApprovalRequestDAL.transaction(async (tx) => {
|
|
||||||
const approvalRequest = await accessApprovalRequestDAL.create(
|
|
||||||
{
|
|
||||||
policyId: policy.id,
|
|
||||||
requestedBy: membership.id,
|
|
||||||
temporaryRange: temporaryRange || null,
|
|
||||||
permissions: JSON.stringify(requestedPermissions),
|
|
||||||
isTemporary
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
|
|
||||||
await smtpService.sendMail({
|
|
||||||
recipients: approverUsers.filter((approver) => approver.email).map((approver) => approver.email!),
|
|
||||||
subjectLine: "Access Approval Request",
|
|
||||||
|
|
||||||
substitutions: {
|
|
||||||
projectName: project.name,
|
|
||||||
requesterFullName: `${requestedByUser.firstName} ${requestedByUser.lastName}`,
|
|
||||||
requesterEmail: requestedByUser.email,
|
|
||||||
isTemporary,
|
|
||||||
...(isTemporary && {
|
|
||||||
expiresIn: ms(ms(temporaryRange || ""), { long: true })
|
|
||||||
}),
|
|
||||||
secretPath,
|
|
||||||
environment: envSlug,
|
|
||||||
permissions: accessTypes,
|
|
||||||
approvalUrl: `${cfg.SITE_URL}/project/${project.id}/approval`
|
|
||||||
},
|
|
||||||
template: SmtpTemplates.AccessApprovalRequest
|
|
||||||
});
|
|
||||||
|
|
||||||
return approvalRequest;
|
|
||||||
});
|
|
||||||
|
|
||||||
return { request: approval };
|
|
||||||
};
|
|
||||||
|
|
||||||
const listApprovalRequests = async ({
|
|
||||||
projectSlug,
|
|
||||||
authorProjectMembershipId,
|
|
||||||
envSlug,
|
|
||||||
actor,
|
|
||||||
actorOrgId,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod
|
|
||||||
}: TListApprovalRequestsDTO) => {
|
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
|
||||||
if (!project) throw new UnauthorizedError({ message: "Project not found" });
|
|
||||||
|
|
||||||
const { membership } = await permissionService.getProjectPermission(
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
project.id,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
if (!membership) throw new UnauthorizedError({ message: "You are not a member of this project" });
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { requests };
|
|
||||||
};
|
|
||||||
|
|
||||||
const reviewAccessRequest = async ({
|
|
||||||
requestId,
|
|
||||||
actor,
|
|
||||||
status,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
}: TReviewAccessRequestDTO) => {
|
|
||||||
const accessApprovalRequest = await accessApprovalRequestDAL.findById(requestId);
|
|
||||||
if (!accessApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
|
|
||||||
|
|
||||||
const { policy } = accessApprovalRequest;
|
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
accessApprovalRequest.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!membership) throw new UnauthorizedError({ message: "You are not a member of this project" });
|
|
||||||
|
|
||||||
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
|
|
||||||
) {
|
|
||||||
throw new UnauthorizedError({ message: "You are not authorized to approve this request" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const reviewerProjectMembership = await projectMembershipDAL.findById(membership.id);
|
|
||||||
|
|
||||||
await verifyApprovers({
|
|
||||||
projectId: accessApprovalRequest.projectId,
|
|
||||||
orgId: actorOrgId,
|
|
||||||
envSlug: accessApprovalRequest.environment,
|
|
||||||
secretPath: accessApprovalRequest.policy.secretPath!,
|
|
||||||
actorAuthMethod,
|
|
||||||
permissionService,
|
|
||||||
userIds: [reviewerProjectMembership.userId]
|
|
||||||
});
|
|
||||||
|
|
||||||
const existingReviews = await accessApprovalRequestReviewerDAL.find({ requestId: accessApprovalRequest.id });
|
|
||||||
if (existingReviews.some((review) => review.status === ApprovalStatus.REJECTED)) {
|
|
||||||
throw new BadRequestError({ message: "The request has already been rejected by another reviewer" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const reviewStatus = await accessApprovalRequestReviewerDAL.transaction(async (tx) => {
|
|
||||||
const review = await accessApprovalRequestReviewerDAL.findOne(
|
|
||||||
{
|
|
||||||
requestId: accessApprovalRequest.id,
|
|
||||||
member: membership.id
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
if (!review) {
|
|
||||||
const newReview = await accessApprovalRequestReviewerDAL.create(
|
|
||||||
{
|
|
||||||
status,
|
|
||||||
requestId: accessApprovalRequest.id,
|
|
||||||
member: membership.id
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
|
|
||||||
const allReviews = [...existingReviews, newReview];
|
|
||||||
|
|
||||||
const approvedReviews = allReviews.filter((r) => r.status === ApprovalStatus.APPROVED);
|
|
||||||
|
|
||||||
// approvals is the required number of approvals. If the number of approved reviews is equal to the number of required approvals, then the request is approved.
|
|
||||||
if (approvedReviews.length === policy.approvals) {
|
|
||||||
if (accessApprovalRequest.isTemporary && !accessApprovalRequest.temporaryRange) {
|
|
||||||
throw new BadRequestError({ message: "Temporary range is required for temporary access" });
|
|
||||||
}
|
|
||||||
|
|
||||||
let privilegeId: string | null = null;
|
|
||||||
|
|
||||||
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;
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
await accessApprovalRequestDAL.updateById(accessApprovalRequest.id, { privilegeId }, tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newReview;
|
|
||||||
}
|
|
||||||
throw new BadRequestError({ message: "You have already reviewed this request" });
|
|
||||||
});
|
|
||||||
|
|
||||||
return reviewStatus;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCount = async ({ projectSlug, actor, actorAuthMethod, actorId, actorOrgId }: TGetAccessRequestCountDTO) => {
|
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
|
||||||
if (!project) throw new UnauthorizedError({ message: "Project not found" });
|
|
||||||
|
|
||||||
const { membership } = await permissionService.getProjectPermission(
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
project.id,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
if (!membership) throw new BadRequestError({ message: "User not found in project" });
|
|
||||||
|
|
||||||
const count = await accessApprovalRequestDAL.getCount({ projectId: project.id });
|
|
||||||
|
|
||||||
return { count };
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
createAccessApprovalRequest,
|
|
||||||
listApprovalRequests,
|
|
||||||
reviewAccessRequest,
|
|
||||||
getCount
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,33 +0,0 @@
|
|||||||
import { TProjectPermission } from "@app/lib/types";
|
|
||||||
|
|
||||||
export enum ApprovalStatus {
|
|
||||||
PENDING = "pending",
|
|
||||||
APPROVED = "approved",
|
|
||||||
REJECTED = "rejected"
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TVerifyPermission = {
|
|
||||||
permissions: unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TGetAccessRequestCountDTO = {
|
|
||||||
projectSlug: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TReviewAccessRequestDTO = {
|
|
||||||
requestId: string;
|
|
||||||
status: ApprovalStatus;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TCreateAccessApprovalRequestDTO = {
|
|
||||||
projectSlug: string;
|
|
||||||
permissions: unknown;
|
|
||||||
isTemporary: boolean;
|
|
||||||
temporaryRange?: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TListApprovalRequestsDTO = {
|
|
||||||
projectSlug: string;
|
|
||||||
authorProjectMembershipId?: string;
|
|
||||||
envSlug?: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
@ -1,11 +0,0 @@
|
|||||||
import { TDbClient } from "@app/db";
|
|
||||||
import { TableName } from "@app/db/schemas";
|
|
||||||
import { ormify } from "@app/lib/knex";
|
|
||||||
|
|
||||||
export type TAuditLogStreamDALFactory = ReturnType<typeof auditLogStreamDALFactory>;
|
|
||||||
|
|
||||||
export const auditLogStreamDALFactory = (db: TDbClient) => {
|
|
||||||
const orm = ormify(db, TableName.AuditLogStream);
|
|
||||||
|
|
||||||
return orm;
|
|
||||||
};
|
|
@ -1,233 +0,0 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
|
||||||
import { RawAxiosRequestHeaders } from "axios";
|
|
||||||
|
|
||||||
import { SecretKeyEncoding } from "@app/db/schemas";
|
|
||||||
import { request } from "@app/lib/config/request";
|
|
||||||
import { infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
|
||||||
import { validateLocalIps } from "@app/lib/validator";
|
|
||||||
|
|
||||||
import { AUDIT_LOG_STREAM_TIMEOUT } from "../audit-log/audit-log-queue";
|
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
|
||||||
import { TAuditLogStreamDALFactory } from "./audit-log-stream-dal";
|
|
||||||
import {
|
|
||||||
LogStreamHeaders,
|
|
||||||
TCreateAuditLogStreamDTO,
|
|
||||||
TDeleteAuditLogStreamDTO,
|
|
||||||
TGetDetailsAuditLogStreamDTO,
|
|
||||||
TListAuditLogStreamDTO,
|
|
||||||
TUpdateAuditLogStreamDTO
|
|
||||||
} from "./audit-log-stream-types";
|
|
||||||
|
|
||||||
type TAuditLogStreamServiceFactoryDep = {
|
|
||||||
auditLogStreamDAL: TAuditLogStreamDALFactory;
|
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TAuditLogStreamServiceFactory = ReturnType<typeof auditLogStreamServiceFactory>;
|
|
||||||
|
|
||||||
export const auditLogStreamServiceFactory = ({
|
|
||||||
auditLogStreamDAL,
|
|
||||||
permissionService,
|
|
||||||
licenseService
|
|
||||||
}: TAuditLogStreamServiceFactoryDep) => {
|
|
||||||
const create = async ({
|
|
||||||
url,
|
|
||||||
actor,
|
|
||||||
headers = [],
|
|
||||||
actorId,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod
|
|
||||||
}: TCreateAuditLogStreamDTO) => {
|
|
||||||
if (!actorOrgId) throw new BadRequestError({ message: "Missing org id from token" });
|
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(actorOrgId);
|
|
||||||
if (!plan.auditLogStreams)
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: "Failed to create audit log streams due to plan restriction. Upgrade plan to create group."
|
|
||||||
});
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Settings);
|
|
||||||
|
|
||||||
validateLocalIps(url);
|
|
||||||
|
|
||||||
const totalStreams = await auditLogStreamDAL.find({ orgId: actorOrgId });
|
|
||||||
if (totalStreams.length >= plan.auditLogStreamLimit) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message:
|
|
||||||
"Failed to create audit log streams due to plan limit reached. Kindly contact Infisical to add more streams."
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// testing connection first
|
|
||||||
const streamHeaders: RawAxiosRequestHeaders = { "Content-Type": "application/json" };
|
|
||||||
if (headers.length)
|
|
||||||
headers.forEach(({ key, value }) => {
|
|
||||||
streamHeaders[key] = value;
|
|
||||||
});
|
|
||||||
await request
|
|
||||||
.post(
|
|
||||||
url,
|
|
||||||
{ ping: "ok" },
|
|
||||||
{
|
|
||||||
headers: streamHeaders,
|
|
||||||
// request timeout
|
|
||||||
timeout: AUDIT_LOG_STREAM_TIMEOUT,
|
|
||||||
// connection timeout
|
|
||||||
signal: AbortSignal.timeout(AUDIT_LOG_STREAM_TIMEOUT)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.catch((err) => {
|
|
||||||
throw new Error(`Failed to connect with the source ${(err as Error)?.message}`);
|
|
||||||
});
|
|
||||||
const encryptedHeaders = headers ? infisicalSymmetricEncypt(JSON.stringify(headers)) : undefined;
|
|
||||||
const logStream = await auditLogStreamDAL.create({
|
|
||||||
orgId: actorOrgId,
|
|
||||||
url,
|
|
||||||
...(encryptedHeaders
|
|
||||||
? {
|
|
||||||
encryptedHeadersCiphertext: encryptedHeaders.ciphertext,
|
|
||||||
encryptedHeadersIV: encryptedHeaders.iv,
|
|
||||||
encryptedHeadersTag: encryptedHeaders.tag,
|
|
||||||
encryptedHeadersAlgorithm: encryptedHeaders.algorithm,
|
|
||||||
encryptedHeadersKeyEncoding: encryptedHeaders.encoding
|
|
||||||
}
|
|
||||||
: {})
|
|
||||||
});
|
|
||||||
return logStream;
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateById = async ({
|
|
||||||
id,
|
|
||||||
url,
|
|
||||||
actor,
|
|
||||||
headers = [],
|
|
||||||
actorId,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod
|
|
||||||
}: TUpdateAuditLogStreamDTO) => {
|
|
||||||
if (!actorOrgId) throw new BadRequestError({ message: "Missing org id from token" });
|
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(actorOrgId);
|
|
||||||
if (!plan.auditLogStreams)
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: "Failed to update audit log streams due to plan restriction. Upgrade plan to create group."
|
|
||||||
});
|
|
||||||
|
|
||||||
const logStream = await auditLogStreamDAL.findById(id);
|
|
||||||
if (!logStream) throw new BadRequestError({ message: "Audit log stream not found" });
|
|
||||||
|
|
||||||
const { orgId } = logStream;
|
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
|
|
||||||
|
|
||||||
if (url) validateLocalIps(url);
|
|
||||||
|
|
||||||
// testing connection first
|
|
||||||
const streamHeaders: RawAxiosRequestHeaders = { "Content-Type": "application/json" };
|
|
||||||
if (headers.length)
|
|
||||||
headers.forEach(({ key, value }) => {
|
|
||||||
streamHeaders[key] = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
await request
|
|
||||||
.post(
|
|
||||||
url || logStream.url,
|
|
||||||
{ ping: "ok" },
|
|
||||||
{
|
|
||||||
headers: streamHeaders,
|
|
||||||
// request timeout
|
|
||||||
timeout: AUDIT_LOG_STREAM_TIMEOUT,
|
|
||||||
// connection timeout
|
|
||||||
signal: AbortSignal.timeout(AUDIT_LOG_STREAM_TIMEOUT)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.catch((err) => {
|
|
||||||
throw new Error(`Failed to connect with the source ${(err as Error)?.message}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
const encryptedHeaders = headers ? infisicalSymmetricEncypt(JSON.stringify(headers)) : undefined;
|
|
||||||
const updatedLogStream = await auditLogStreamDAL.updateById(id, {
|
|
||||||
url,
|
|
||||||
...(encryptedHeaders
|
|
||||||
? {
|
|
||||||
encryptedHeadersCiphertext: encryptedHeaders.ciphertext,
|
|
||||||
encryptedHeadersIV: encryptedHeaders.iv,
|
|
||||||
encryptedHeadersTag: encryptedHeaders.tag,
|
|
||||||
encryptedHeadersAlgorithm: encryptedHeaders.algorithm,
|
|
||||||
encryptedHeadersKeyEncoding: encryptedHeaders.encoding
|
|
||||||
}
|
|
||||||
: {})
|
|
||||||
});
|
|
||||||
return updatedLogStream;
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteById = async ({ id, actor, actorId, actorOrgId, actorAuthMethod }: TDeleteAuditLogStreamDTO) => {
|
|
||||||
if (!actorOrgId) throw new BadRequestError({ message: "Missing org id from token" });
|
|
||||||
|
|
||||||
const logStream = await auditLogStreamDAL.findById(id);
|
|
||||||
if (!logStream) throw new BadRequestError({ message: "Audit log stream not found" });
|
|
||||||
|
|
||||||
const { orgId } = logStream;
|
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Settings);
|
|
||||||
|
|
||||||
const deletedLogStream = await auditLogStreamDAL.deleteById(id);
|
|
||||||
return deletedLogStream;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getById = async ({ id, actor, actorId, actorOrgId, actorAuthMethod }: TGetDetailsAuditLogStreamDTO) => {
|
|
||||||
const logStream = await auditLogStreamDAL.findById(id);
|
|
||||||
if (!logStream) throw new BadRequestError({ message: "Audit log stream not found" });
|
|
||||||
|
|
||||||
const { orgId } = logStream;
|
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
|
||||||
|
|
||||||
const headers =
|
|
||||||
logStream?.encryptedHeadersCiphertext && logStream?.encryptedHeadersIV && logStream?.encryptedHeadersTag
|
|
||||||
? (JSON.parse(
|
|
||||||
infisicalSymmetricDecrypt({
|
|
||||||
tag: logStream.encryptedHeadersTag,
|
|
||||||
iv: logStream.encryptedHeadersIV,
|
|
||||||
ciphertext: logStream.encryptedHeadersCiphertext,
|
|
||||||
keyEncoding: logStream.encryptedHeadersKeyEncoding as SecretKeyEncoding
|
|
||||||
})
|
|
||||||
) as LogStreamHeaders[])
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return { ...logStream, headers };
|
|
||||||
};
|
|
||||||
|
|
||||||
const list = async ({ actor, actorId, actorOrgId, actorAuthMethod }: TListAuditLogStreamDTO) => {
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
|
||||||
|
|
||||||
const logStreams = await auditLogStreamDAL.find({ orgId: actorOrgId });
|
|
||||||
return logStreams;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
create,
|
|
||||||
updateById,
|
|
||||||
deleteById,
|
|
||||||
getById,
|
|
||||||
list
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,27 +0,0 @@
|
|||||||
import { TOrgPermission } from "@app/lib/types";
|
|
||||||
|
|
||||||
export type LogStreamHeaders = {
|
|
||||||
key: string;
|
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TCreateAuditLogStreamDTO = Omit<TOrgPermission, "orgId"> & {
|
|
||||||
url: string;
|
|
||||||
headers?: LogStreamHeaders[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TUpdateAuditLogStreamDTO = Omit<TOrgPermission, "orgId"> & {
|
|
||||||
id: string;
|
|
||||||
url?: string;
|
|
||||||
headers?: LogStreamHeaders[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TDeleteAuditLogStreamDTO = Omit<TOrgPermission, "orgId"> & {
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TListAuditLogStreamDTO = Omit<TOrgPermission, "orgId">;
|
|
||||||
|
|
||||||
export type TGetDetailsAuditLogStreamDTO = Omit<TOrgPermission, "orgId"> & {
|
|
||||||
id: string;
|
|
||||||
};
|
|
@ -1,20 +1,13 @@
|
|||||||
import { RawAxiosRequestHeaders } from "axios";
|
import { logger } from "@app/lib/logger";
|
||||||
|
|
||||||
import { SecretKeyEncoding } from "@app/db/schemas";
|
|
||||||
import { request } from "@app/lib/config/request";
|
|
||||||
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
|
||||||
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
import { TAuditLogStreamDALFactory } from "../audit-log-stream/audit-log-stream-dal";
|
|
||||||
import { LogStreamHeaders } from "../audit-log-stream/audit-log-stream-types";
|
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { TAuditLogDALFactory } from "./audit-log-dal";
|
import { TAuditLogDALFactory } from "./audit-log-dal";
|
||||||
import { TCreateAuditLogDTO } from "./audit-log-types";
|
import { TCreateAuditLogDTO } from "./audit-log-types";
|
||||||
|
|
||||||
type TAuditLogQueueServiceFactoryDep = {
|
type TAuditLogQueueServiceFactoryDep = {
|
||||||
auditLogDAL: TAuditLogDALFactory;
|
auditLogDAL: TAuditLogDALFactory;
|
||||||
auditLogStreamDAL: Pick<TAuditLogStreamDALFactory, "find">;
|
|
||||||
queueService: TQueueServiceFactory;
|
queueService: TQueueServiceFactory;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findById">;
|
projectDAL: Pick<TProjectDALFactory, "findById">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
@ -22,15 +15,11 @@ type TAuditLogQueueServiceFactoryDep = {
|
|||||||
|
|
||||||
export type TAuditLogQueueServiceFactory = ReturnType<typeof auditLogQueueServiceFactory>;
|
export type TAuditLogQueueServiceFactory = ReturnType<typeof auditLogQueueServiceFactory>;
|
||||||
|
|
||||||
// keep this timeout 5s it must be fast because else the queue will take time to finish
|
|
||||||
// audit log is a crowded queue thus needs to be fast
|
|
||||||
export const AUDIT_LOG_STREAM_TIMEOUT = 5 * 1000;
|
|
||||||
export const auditLogQueueServiceFactory = ({
|
export const auditLogQueueServiceFactory = ({
|
||||||
auditLogDAL,
|
auditLogDAL,
|
||||||
queueService,
|
queueService,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
licenseService,
|
licenseService
|
||||||
auditLogStreamDAL
|
|
||||||
}: TAuditLogQueueServiceFactoryDep) => {
|
}: TAuditLogQueueServiceFactoryDep) => {
|
||||||
const pushToLog = async (data: TCreateAuditLogDTO) => {
|
const pushToLog = async (data: TCreateAuditLogDTO) => {
|
||||||
await queueService.queue(QueueName.AuditLog, QueueJobs.AuditLog, data, {
|
await queueService.queue(QueueName.AuditLog, QueueJobs.AuditLog, data, {
|
||||||
@ -58,7 +47,7 @@ export const auditLogQueueServiceFactory = ({
|
|||||||
// skip inserting if audit log retention is 0 meaning its not supported
|
// skip inserting if audit log retention is 0 meaning its not supported
|
||||||
if (ttl === 0) return;
|
if (ttl === 0) return;
|
||||||
|
|
||||||
const auditLog = await auditLogDAL.create({
|
await auditLogDAL.create({
|
||||||
actor: actor.type,
|
actor: actor.type,
|
||||||
actorMetadata: actor.metadata,
|
actorMetadata: actor.metadata,
|
||||||
userAgent,
|
userAgent,
|
||||||
@ -70,49 +59,37 @@ export const auditLogQueueServiceFactory = ({
|
|||||||
eventMetadata: event.metadata,
|
eventMetadata: event.metadata,
|
||||||
userAgentType
|
userAgentType
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const logStreams = orgId ? await auditLogStreamDAL.find({ orgId }) : [];
|
queueService.start(QueueName.AuditLogPrune, async () => {
|
||||||
await Promise.allSettled(
|
logger.info(`${QueueName.AuditLogPrune}: queue task started`);
|
||||||
logStreams.map(
|
await auditLogDAL.pruneAuditLog();
|
||||||
async ({
|
logger.info(`${QueueName.AuditLogPrune}: queue task completed`);
|
||||||
url,
|
});
|
||||||
encryptedHeadersTag,
|
|
||||||
encryptedHeadersIV,
|
|
||||||
encryptedHeadersKeyEncoding,
|
|
||||||
encryptedHeadersCiphertext
|
|
||||||
}) => {
|
|
||||||
const streamHeaders =
|
|
||||||
encryptedHeadersIV && encryptedHeadersCiphertext && encryptedHeadersTag
|
|
||||||
? (JSON.parse(
|
|
||||||
infisicalSymmetricDecrypt({
|
|
||||||
keyEncoding: encryptedHeadersKeyEncoding as SecretKeyEncoding,
|
|
||||||
iv: encryptedHeadersIV,
|
|
||||||
tag: encryptedHeadersTag,
|
|
||||||
ciphertext: encryptedHeadersCiphertext
|
|
||||||
})
|
|
||||||
) as LogStreamHeaders[])
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const headers: RawAxiosRequestHeaders = { "Content-Type": "application/json" };
|
// we do a repeat cron job in utc timezone at 12 Midnight each day
|
||||||
|
const startAuditLogPruneJob = async () => {
|
||||||
if (streamHeaders.length)
|
// clear previous job
|
||||||
streamHeaders.forEach(({ key, value }) => {
|
await queueService.stopRepeatableJob(
|
||||||
headers[key] = value;
|
QueueName.AuditLogPrune,
|
||||||
});
|
QueueJobs.AuditLogPrune,
|
||||||
|
{ pattern: "0 0 * * *", utc: true },
|
||||||
return request.post(url, auditLog, {
|
QueueName.AuditLogPrune // just a job id
|
||||||
headers,
|
|
||||||
// request timeout
|
|
||||||
timeout: AUDIT_LOG_STREAM_TIMEOUT,
|
|
||||||
// connection timeout
|
|
||||||
signal: AbortSignal.timeout(AUDIT_LOG_STREAM_TIMEOUT)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await queueService.queue(QueueName.AuditLogPrune, QueueJobs.AuditLogPrune, undefined, {
|
||||||
|
delay: 5000,
|
||||||
|
jobId: QueueName.AuditLogPrune,
|
||||||
|
repeat: { pattern: "0 0 * * *", utc: true }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
queueService.listen(QueueName.AuditLogPrune, "failed", (err) => {
|
||||||
|
logger.error(err?.failedReason, `${QueueName.AuditLogPrune}: log pruning failed`);
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pushToLog
|
pushToLog,
|
||||||
|
startAuditLogPruneJob
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -51,7 +51,6 @@ export enum EventType {
|
|||||||
UNAUTHORIZE_INTEGRATION = "unauthorize-integration",
|
UNAUTHORIZE_INTEGRATION = "unauthorize-integration",
|
||||||
CREATE_INTEGRATION = "create-integration",
|
CREATE_INTEGRATION = "create-integration",
|
||||||
DELETE_INTEGRATION = "delete-integration",
|
DELETE_INTEGRATION = "delete-integration",
|
||||||
MANUAL_SYNC_INTEGRATION = "manual-sync-integration",
|
|
||||||
ADD_TRUSTED_IP = "add-trusted-ip",
|
ADD_TRUSTED_IP = "add-trusted-ip",
|
||||||
UPDATE_TRUSTED_IP = "update-trusted-ip",
|
UPDATE_TRUSTED_IP = "update-trusted-ip",
|
||||||
DELETE_TRUSTED_IP = "delete-trusted-ip",
|
DELETE_TRUSTED_IP = "delete-trusted-ip",
|
||||||
@ -64,25 +63,9 @@ export enum EventType {
|
|||||||
ADD_IDENTITY_UNIVERSAL_AUTH = "add-identity-universal-auth",
|
ADD_IDENTITY_UNIVERSAL_AUTH = "add-identity-universal-auth",
|
||||||
UPDATE_IDENTITY_UNIVERSAL_AUTH = "update-identity-universal-auth",
|
UPDATE_IDENTITY_UNIVERSAL_AUTH = "update-identity-universal-auth",
|
||||||
GET_IDENTITY_UNIVERSAL_AUTH = "get-identity-universal-auth",
|
GET_IDENTITY_UNIVERSAL_AUTH = "get-identity-universal-auth",
|
||||||
LOGIN_IDENTITY_KUBERNETES_AUTH = "login-identity-kubernetes-auth",
|
|
||||||
ADD_IDENTITY_KUBERNETES_AUTH = "add-identity-kubernetes-auth",
|
|
||||||
UPDATE_IDENTITY_KUBENETES_AUTH = "update-identity-kubernetes-auth",
|
|
||||||
GET_IDENTITY_KUBERNETES_AUTH = "get-identity-kubernetes-auth",
|
|
||||||
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
|
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
|
||||||
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
|
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
|
||||||
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
|
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
|
||||||
LOGIN_IDENTITY_GCP_AUTH = "login-identity-gcp-auth",
|
|
||||||
ADD_IDENTITY_GCP_AUTH = "add-identity-gcp-auth",
|
|
||||||
UPDATE_IDENTITY_GCP_AUTH = "update-identity-gcp-auth",
|
|
||||||
GET_IDENTITY_GCP_AUTH = "get-identity-gcp-auth",
|
|
||||||
LOGIN_IDENTITY_AWS_AUTH = "login-identity-aws-auth",
|
|
||||||
ADD_IDENTITY_AWS_AUTH = "add-identity-aws-auth",
|
|
||||||
UPDATE_IDENTITY_AWS_AUTH = "update-identity-aws-auth",
|
|
||||||
GET_IDENTITY_AWS_AUTH = "get-identity-aws-auth",
|
|
||||||
LOGIN_IDENTITY_AZURE_AUTH = "login-identity-azure-auth",
|
|
||||||
ADD_IDENTITY_AZURE_AUTH = "add-identity-azure-auth",
|
|
||||||
UPDATE_IDENTITY_AZURE_AUTH = "update-identity-azure-auth",
|
|
||||||
GET_IDENTITY_AZURE_AUTH = "get-identity-azure-auth",
|
|
||||||
CREATE_ENVIRONMENT = "create-environment",
|
CREATE_ENVIRONMENT = "create-environment",
|
||||||
UPDATE_ENVIRONMENT = "update-environment",
|
UPDATE_ENVIRONMENT = "update-environment",
|
||||||
DELETE_ENVIRONMENT = "delete-environment",
|
DELETE_ENVIRONMENT = "delete-environment",
|
||||||
@ -286,25 +269,6 @@ interface DeleteIntegrationEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ManualSyncIntegrationEvent {
|
|
||||||
type: EventType.MANUAL_SYNC_INTEGRATION;
|
|
||||||
metadata: {
|
|
||||||
integrationId: string;
|
|
||||||
integration: string;
|
|
||||||
environment: string;
|
|
||||||
secretPath: string;
|
|
||||||
url?: string;
|
|
||||||
app?: string;
|
|
||||||
appId?: string;
|
|
||||||
targetEnvironment?: string;
|
|
||||||
targetEnvironmentId?: string;
|
|
||||||
targetService?: string;
|
|
||||||
targetServiceId?: string;
|
|
||||||
path?: string;
|
|
||||||
region?: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AddTrustedIPEvent {
|
interface AddTrustedIPEvent {
|
||||||
type: EventType.ADD_TRUSTED_IP;
|
type: EventType.ADD_TRUSTED_IP;
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -419,50 +383,6 @@ interface GetIdentityUniversalAuthEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LoginIdentityKubernetesAuthEvent {
|
|
||||||
type: EventType.LOGIN_IDENTITY_KUBERNETES_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
identityKubernetesAuthId: string;
|
|
||||||
identityAccessTokenId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AddIdentityKubernetesAuthEvent {
|
|
||||||
type: EventType.ADD_IDENTITY_KUBERNETES_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
kubernetesHost: string;
|
|
||||||
allowedNamespaces: string;
|
|
||||||
allowedNames: string;
|
|
||||||
accessTokenTTL: number;
|
|
||||||
accessTokenMaxTTL: number;
|
|
||||||
accessTokenNumUsesLimit: number;
|
|
||||||
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UpdateIdentityKubernetesAuthEvent {
|
|
||||||
type: EventType.UPDATE_IDENTITY_KUBENETES_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
kubernetesHost?: string;
|
|
||||||
allowedNamespaces?: string;
|
|
||||||
allowedNames?: string;
|
|
||||||
accessTokenTTL?: number;
|
|
||||||
accessTokenMaxTTL?: number;
|
|
||||||
accessTokenNumUsesLimit?: number;
|
|
||||||
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetIdentityKubernetesAuthEvent {
|
|
||||||
type: EventType.GET_IDENTITY_KUBERNETES_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CreateIdentityUniversalAuthClientSecretEvent {
|
interface CreateIdentityUniversalAuthClientSecretEvent {
|
||||||
type: EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET;
|
type: EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET;
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -486,138 +406,6 @@ interface RevokeIdentityUniversalAuthClientSecretEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LoginIdentityGcpAuthEvent {
|
|
||||||
type: EventType.LOGIN_IDENTITY_GCP_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
identityGcpAuthId: string;
|
|
||||||
identityAccessTokenId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AddIdentityGcpAuthEvent {
|
|
||||||
type: EventType.ADD_IDENTITY_GCP_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
type: string;
|
|
||||||
allowedServiceAccounts: string;
|
|
||||||
allowedProjects: string;
|
|
||||||
allowedZones: string;
|
|
||||||
accessTokenTTL: number;
|
|
||||||
accessTokenMaxTTL: number;
|
|
||||||
accessTokenNumUsesLimit: number;
|
|
||||||
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UpdateIdentityGcpAuthEvent {
|
|
||||||
type: EventType.UPDATE_IDENTITY_GCP_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
type?: string;
|
|
||||||
allowedServiceAccounts?: string;
|
|
||||||
allowedProjects?: string;
|
|
||||||
allowedZones?: string;
|
|
||||||
accessTokenTTL?: number;
|
|
||||||
accessTokenMaxTTL?: number;
|
|
||||||
accessTokenNumUsesLimit?: number;
|
|
||||||
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetIdentityGcpAuthEvent {
|
|
||||||
type: EventType.GET_IDENTITY_GCP_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LoginIdentityAwsAuthEvent {
|
|
||||||
type: EventType.LOGIN_IDENTITY_AWS_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
identityAwsAuthId: string;
|
|
||||||
identityAccessTokenId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AddIdentityAwsAuthEvent {
|
|
||||||
type: EventType.ADD_IDENTITY_AWS_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
stsEndpoint: string;
|
|
||||||
allowedPrincipalArns: string;
|
|
||||||
allowedAccountIds: string;
|
|
||||||
accessTokenTTL: number;
|
|
||||||
accessTokenMaxTTL: number;
|
|
||||||
accessTokenNumUsesLimit: number;
|
|
||||||
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UpdateIdentityAwsAuthEvent {
|
|
||||||
type: EventType.UPDATE_IDENTITY_AWS_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
stsEndpoint?: string;
|
|
||||||
allowedPrincipalArns?: string;
|
|
||||||
allowedAccountIds?: string;
|
|
||||||
accessTokenTTL?: number;
|
|
||||||
accessTokenMaxTTL?: number;
|
|
||||||
accessTokenNumUsesLimit?: number;
|
|
||||||
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetIdentityAwsAuthEvent {
|
|
||||||
type: EventType.GET_IDENTITY_AWS_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LoginIdentityAzureAuthEvent {
|
|
||||||
type: EventType.LOGIN_IDENTITY_AZURE_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
identityAzureAuthId: string;
|
|
||||||
identityAccessTokenId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AddIdentityAzureAuthEvent {
|
|
||||||
type: EventType.ADD_IDENTITY_AZURE_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
tenantId: string;
|
|
||||||
resource: string;
|
|
||||||
accessTokenTTL: number;
|
|
||||||
accessTokenMaxTTL: number;
|
|
||||||
accessTokenNumUsesLimit: number;
|
|
||||||
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UpdateIdentityAzureAuthEvent {
|
|
||||||
type: EventType.UPDATE_IDENTITY_AZURE_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
tenantId?: string;
|
|
||||||
resource?: string;
|
|
||||||
accessTokenTTL?: number;
|
|
||||||
accessTokenMaxTTL?: number;
|
|
||||||
accessTokenNumUsesLimit?: number;
|
|
||||||
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetIdentityAzureAuthEvent {
|
|
||||||
type: EventType.GET_IDENTITY_AZURE_AUTH;
|
|
||||||
metadata: {
|
|
||||||
identityId: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CreateEnvironmentEvent {
|
interface CreateEnvironmentEvent {
|
||||||
type: EventType.CREATE_ENVIRONMENT;
|
type: EventType.CREATE_ENVIRONMENT;
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -857,7 +645,6 @@ export type Event =
|
|||||||
| UnauthorizeIntegrationEvent
|
| UnauthorizeIntegrationEvent
|
||||||
| CreateIntegrationEvent
|
| CreateIntegrationEvent
|
||||||
| DeleteIntegrationEvent
|
| DeleteIntegrationEvent
|
||||||
| ManualSyncIntegrationEvent
|
|
||||||
| AddTrustedIPEvent
|
| AddTrustedIPEvent
|
||||||
| UpdateTrustedIPEvent
|
| UpdateTrustedIPEvent
|
||||||
| DeleteTrustedIPEvent
|
| DeleteTrustedIPEvent
|
||||||
@ -870,25 +657,9 @@ export type Event =
|
|||||||
| AddIdentityUniversalAuthEvent
|
| AddIdentityUniversalAuthEvent
|
||||||
| UpdateIdentityUniversalAuthEvent
|
| UpdateIdentityUniversalAuthEvent
|
||||||
| GetIdentityUniversalAuthEvent
|
| GetIdentityUniversalAuthEvent
|
||||||
| LoginIdentityKubernetesAuthEvent
|
|
||||||
| AddIdentityKubernetesAuthEvent
|
|
||||||
| UpdateIdentityKubernetesAuthEvent
|
|
||||||
| GetIdentityKubernetesAuthEvent
|
|
||||||
| CreateIdentityUniversalAuthClientSecretEvent
|
| CreateIdentityUniversalAuthClientSecretEvent
|
||||||
| GetIdentityUniversalAuthClientSecretsEvent
|
| GetIdentityUniversalAuthClientSecretsEvent
|
||||||
| RevokeIdentityUniversalAuthClientSecretEvent
|
| RevokeIdentityUniversalAuthClientSecretEvent
|
||||||
| LoginIdentityGcpAuthEvent
|
|
||||||
| AddIdentityGcpAuthEvent
|
|
||||||
| UpdateIdentityGcpAuthEvent
|
|
||||||
| GetIdentityGcpAuthEvent
|
|
||||||
| LoginIdentityAwsAuthEvent
|
|
||||||
| AddIdentityAwsAuthEvent
|
|
||||||
| UpdateIdentityAwsAuthEvent
|
|
||||||
| GetIdentityAwsAuthEvent
|
|
||||||
| LoginIdentityAzureAuthEvent
|
|
||||||
| AddIdentityAzureAuthEvent
|
|
||||||
| UpdateIdentityAzureAuthEvent
|
|
||||||
| GetIdentityAzureAuthEvent
|
|
||||||
| CreateEnvironmentEvent
|
| CreateEnvironmentEvent
|
||||||
| UpdateEnvironmentEvent
|
| UpdateEnvironmentEvent
|
||||||
| DeleteEnvironmentEvent
|
| DeleteEnvironmentEvent
|
||||||
|
@ -1,194 +0,0 @@
|
|||||||
import {
|
|
||||||
AddUserToGroupCommand,
|
|
||||||
AttachUserPolicyCommand,
|
|
||||||
CreateAccessKeyCommand,
|
|
||||||
CreateUserCommand,
|
|
||||||
DeleteAccessKeyCommand,
|
|
||||||
DeleteUserCommand,
|
|
||||||
DeleteUserPolicyCommand,
|
|
||||||
DetachUserPolicyCommand,
|
|
||||||
GetUserCommand,
|
|
||||||
IAMClient,
|
|
||||||
ListAccessKeysCommand,
|
|
||||||
ListAttachedUserPoliciesCommand,
|
|
||||||
ListGroupsForUserCommand,
|
|
||||||
ListUserPoliciesCommand,
|
|
||||||
PutUserPolicyCommand,
|
|
||||||
RemoveUserFromGroupCommand
|
|
||||||
} from "@aws-sdk/client-iam";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
|
||||||
|
|
||||||
import { DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models";
|
|
||||||
|
|
||||||
const generateUsername = () => {
|
|
||||||
return alphaNumericNanoId(32);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AwsIamProvider = (): TDynamicProviderFns => {
|
|
||||||
const validateProviderInputs = async (inputs: unknown) => {
|
|
||||||
const providerInputs = await DynamicSecretAwsIamSchema.parseAsync(inputs);
|
|
||||||
return providerInputs;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getClient = async (providerInputs: z.infer<typeof DynamicSecretAwsIamSchema>) => {
|
|
||||||
const client = new IAMClient({
|
|
||||||
region: providerInputs.region,
|
|
||||||
credentials: {
|
|
||||||
accessKeyId: providerInputs.accessKey,
|
|
||||||
secretAccessKey: providerInputs.secretAccessKey
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return client;
|
|
||||||
};
|
|
||||||
|
|
||||||
const validateConnection = async (inputs: unknown) => {
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
|
||||||
const client = await getClient(providerInputs);
|
|
||||||
|
|
||||||
const isConnected = await client.send(new GetUserCommand({})).then(() => true);
|
|
||||||
return isConnected;
|
|
||||||
};
|
|
||||||
|
|
||||||
const create = async (inputs: unknown) => {
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
|
||||||
const client = await getClient(providerInputs);
|
|
||||||
|
|
||||||
const username = generateUsername();
|
|
||||||
const { policyArns, userGroups, policyDocument, awsPath, permissionBoundaryPolicyArn } = providerInputs;
|
|
||||||
const createUserRes = await client.send(
|
|
||||||
new CreateUserCommand({
|
|
||||||
Path: awsPath,
|
|
||||||
PermissionsBoundary: permissionBoundaryPolicyArn || undefined,
|
|
||||||
Tags: [{ Key: "createdBy", Value: "infisical-dynamic-secret" }],
|
|
||||||
UserName: username
|
|
||||||
})
|
|
||||||
);
|
|
||||||
if (!createUserRes.User) throw new BadRequestError({ message: "Failed to create AWS IAM User" });
|
|
||||||
if (userGroups) {
|
|
||||||
await Promise.all(
|
|
||||||
userGroups
|
|
||||||
.split(",")
|
|
||||||
.filter(Boolean)
|
|
||||||
.map((group) =>
|
|
||||||
client.send(new AddUserToGroupCommand({ UserName: createUserRes?.User?.UserName, GroupName: group }))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (policyArns) {
|
|
||||||
await Promise.all(
|
|
||||||
policyArns
|
|
||||||
.split(",")
|
|
||||||
.filter(Boolean)
|
|
||||||
.map((policyArn) =>
|
|
||||||
client.send(new AttachUserPolicyCommand({ UserName: createUserRes?.User?.UserName, PolicyArn: policyArn }))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (policyDocument) {
|
|
||||||
await client.send(
|
|
||||||
new PutUserPolicyCommand({
|
|
||||||
UserName: createUserRes.User.UserName,
|
|
||||||
PolicyName: `infisical-dynamic-policy-${alphaNumericNanoId(4)}`,
|
|
||||||
PolicyDocument: policyDocument
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const createAccessKeyRes = await client.send(
|
|
||||||
new CreateAccessKeyCommand({
|
|
||||||
UserName: createUserRes.User.UserName
|
|
||||||
})
|
|
||||||
);
|
|
||||||
if (!createAccessKeyRes.AccessKey)
|
|
||||||
throw new BadRequestError({ message: "Failed to create AWS IAM User access key" });
|
|
||||||
|
|
||||||
return {
|
|
||||||
entityId: username,
|
|
||||||
data: {
|
|
||||||
ACCESS_KEY: createAccessKeyRes.AccessKey.AccessKeyId,
|
|
||||||
SECRET_ACCESS_KEY: createAccessKeyRes.AccessKey.SecretAccessKey,
|
|
||||||
USERNAME: username
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const revoke = async (inputs: unknown, entityId: string) => {
|
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
|
||||||
const client = await getClient(providerInputs);
|
|
||||||
|
|
||||||
const username = entityId;
|
|
||||||
|
|
||||||
// remove user from groups
|
|
||||||
const userGroups = await client.send(new ListGroupsForUserCommand({ UserName: username }));
|
|
||||||
await Promise.all(
|
|
||||||
(userGroups.Groups || []).map(({ GroupName }) =>
|
|
||||||
client.send(
|
|
||||||
new RemoveUserFromGroupCommand({
|
|
||||||
GroupName,
|
|
||||||
UserName: username
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// remove user access keys
|
|
||||||
const userAccessKeys = await client.send(new ListAccessKeysCommand({ UserName: username }));
|
|
||||||
await Promise.all(
|
|
||||||
(userAccessKeys.AccessKeyMetadata || []).map(({ AccessKeyId }) =>
|
|
||||||
client.send(
|
|
||||||
new DeleteAccessKeyCommand({
|
|
||||||
AccessKeyId,
|
|
||||||
UserName: username
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// remove user inline policies
|
|
||||||
const userInlinePolicies = await client.send(new ListUserPoliciesCommand({ UserName: username }));
|
|
||||||
await Promise.all(
|
|
||||||
(userInlinePolicies.PolicyNames || []).map((policyName) =>
|
|
||||||
client.send(
|
|
||||||
new DeleteUserPolicyCommand({
|
|
||||||
PolicyName: policyName,
|
|
||||||
UserName: username
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// remove user attached policies
|
|
||||||
const userAttachedPolicies = await client.send(new ListAttachedUserPoliciesCommand({ UserName: username }));
|
|
||||||
await Promise.all(
|
|
||||||
(userAttachedPolicies.AttachedPolicies || []).map((policy) =>
|
|
||||||
client.send(
|
|
||||||
new DetachUserPolicyCommand({
|
|
||||||
PolicyArn: policy.PolicyArn,
|
|
||||||
UserName: username
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
await client.send(new DeleteUserCommand({ UserName: username }));
|
|
||||||
return { entityId: username };
|
|
||||||
};
|
|
||||||
|
|
||||||
const renew = async (_inputs: unknown, entityId: string) => {
|
|
||||||
// do nothing
|
|
||||||
const username = entityId;
|
|
||||||
return { entityId: username };
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
validateProviderInputs,
|
|
||||||
validateConnection,
|
|
||||||
create,
|
|
||||||
revoke,
|
|
||||||
renew
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,10 +1,8 @@
|
|||||||
import { AwsIamProvider } from "./aws-iam";
|
|
||||||
import { CassandraProvider } from "./cassandra";
|
import { CassandraProvider } from "./cassandra";
|
||||||
import { DynamicSecretProviders } from "./models";
|
import { DynamicSecretProviders } from "./models";
|
||||||
import { SqlDatabaseProvider } from "./sql-database";
|
import { SqlDatabaseProvider } from "./sql-database";
|
||||||
|
|
||||||
export const buildDynamicSecretProviders = () => ({
|
export const buildDynamicSecretProviders = () => ({
|
||||||
[DynamicSecretProviders.SqlDatabase]: SqlDatabaseProvider(),
|
[DynamicSecretProviders.SqlDatabase]: SqlDatabaseProvider(),
|
||||||
[DynamicSecretProviders.Cassandra]: CassandraProvider(),
|
[DynamicSecretProviders.Cassandra]: CassandraProvider()
|
||||||
[DynamicSecretProviders.AwsIam]: AwsIamProvider()
|
|
||||||
});
|
});
|
||||||
|
@ -8,51 +8,38 @@ export enum SqlProviders {
|
|||||||
|
|
||||||
export const DynamicSecretSqlDBSchema = z.object({
|
export const DynamicSecretSqlDBSchema = z.object({
|
||||||
client: z.nativeEnum(SqlProviders),
|
client: z.nativeEnum(SqlProviders),
|
||||||
host: z.string().trim().toLowerCase(),
|
host: z.string().toLowerCase(),
|
||||||
port: z.number(),
|
port: z.number(),
|
||||||
database: z.string().trim(),
|
database: z.string(),
|
||||||
username: z.string().trim(),
|
username: z.string(),
|
||||||
password: z.string().trim(),
|
password: z.string(),
|
||||||
creationStatement: z.string().trim(),
|
creationStatement: z.string(),
|
||||||
revocationStatement: z.string().trim(),
|
revocationStatement: z.string(),
|
||||||
renewStatement: z.string().trim().optional(),
|
renewStatement: z.string().optional(),
|
||||||
ca: z.string().optional()
|
ca: z.string().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const DynamicSecretCassandraSchema = z.object({
|
export const DynamicSecretCassandraSchema = z.object({
|
||||||
host: z.string().trim().toLowerCase(),
|
host: z.string().toLowerCase(),
|
||||||
port: z.number(),
|
port: z.number(),
|
||||||
localDataCenter: z.string().trim().min(1),
|
localDataCenter: z.string().min(1),
|
||||||
keyspace: z.string().trim().optional(),
|
keyspace: z.string().optional(),
|
||||||
username: z.string().trim(),
|
username: z.string(),
|
||||||
password: z.string().trim(),
|
password: z.string(),
|
||||||
creationStatement: z.string().trim(),
|
creationStatement: z.string(),
|
||||||
revocationStatement: z.string().trim(),
|
revocationStatement: z.string(),
|
||||||
renewStatement: z.string().trim().optional(),
|
renewStatement: z.string().optional(),
|
||||||
ca: z.string().optional()
|
ca: z.string().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const DynamicSecretAwsIamSchema = z.object({
|
|
||||||
accessKey: z.string().trim().min(1),
|
|
||||||
secretAccessKey: z.string().trim().min(1),
|
|
||||||
region: z.string().trim().min(1),
|
|
||||||
awsPath: z.string().trim().optional(),
|
|
||||||
permissionBoundaryPolicyArn: z.string().trim().optional(),
|
|
||||||
policyDocument: z.string().trim().optional(),
|
|
||||||
userGroups: z.string().trim().optional(),
|
|
||||||
policyArns: z.string().trim().optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
export enum DynamicSecretProviders {
|
export enum DynamicSecretProviders {
|
||||||
SqlDatabase = "sql-database",
|
SqlDatabase = "sql-database",
|
||||||
Cassandra = "cassandra",
|
Cassandra = "cassandra"
|
||||||
AwsIam = "aws-iam"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DynamicSecretProviderSchema = z.discriminatedUnion("type", [
|
export const DynamicSecretProviderSchema = z.discriminatedUnion("type", [
|
||||||
z.object({ type: z.literal(DynamicSecretProviders.SqlDatabase), inputs: DynamicSecretSqlDBSchema }),
|
z.object({ type: z.literal(DynamicSecretProviders.SqlDatabase), inputs: DynamicSecretSqlDBSchema }),
|
||||||
z.object({ type: z.literal(DynamicSecretProviders.Cassandra), inputs: DynamicSecretCassandraSchema }),
|
z.object({ type: z.literal(DynamicSecretProviders.Cassandra), inputs: DynamicSecretCassandraSchema })
|
||||||
z.object({ type: z.literal(DynamicSecretProviders.AwsIam), inputs: DynamicSecretAwsIamSchema })
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export type TDynamicProviderFns = {
|
export type TDynamicProviderFns = {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { SecretKeyEncoding, TableName, TUsers } from "@app/db/schemas";
|
import { SecretKeyEncoding, TUsers } from "@app/db/schemas";
|
||||||
import { decryptAsymmetric, encryptAsymmetric, infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
import { decryptAsymmetric, encryptAsymmetric, infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
||||||
import { BadRequestError, ScimRequestError } from "@app/lib/errors";
|
import { BadRequestError, ScimRequestError } from "@app/lib/errors";
|
||||||
|
|
||||||
@ -188,9 +188,9 @@ export const addUsersToGroupByUserIds = async ({
|
|||||||
// check if all user(s) are part of the organization
|
// check if all user(s) are part of the organization
|
||||||
const existingUserOrgMemberships = await orgDAL.findMembership(
|
const existingUserOrgMemberships = await orgDAL.findMembership(
|
||||||
{
|
{
|
||||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: group.orgId,
|
orgId: group.orgId,
|
||||||
$in: {
|
$in: {
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: userIds
|
userId: userIds
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { PackRule, unpackRules } from "@casl/ability/extra";
|
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||||
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
|
||||||
@ -10,7 +8,7 @@ import { TIdentityProjectDALFactory } from "@app/services/identity-project/ident
|
|||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSet, ProjectPermissionSub } from "../permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||||
import { TIdentityProjectAdditionalPrivilegeDALFactory } from "./identity-project-additional-privilege-dal";
|
import { TIdentityProjectAdditionalPrivilegeDALFactory } from "./identity-project-additional-privilege-dal";
|
||||||
import {
|
import {
|
||||||
IdentityProjectAdditionalPrivilegeTemporaryMode,
|
IdentityProjectAdditionalPrivilegeTemporaryMode,
|
||||||
@ -32,27 +30,6 @@ export type TIdentityProjectAdditionalPrivilegeServiceFactory = ReturnType<
|
|||||||
typeof identityProjectAdditionalPrivilegeServiceFactory
|
typeof identityProjectAdditionalPrivilegeServiceFactory
|
||||||
>;
|
>;
|
||||||
|
|
||||||
// TODO(akhilmhdh): move this to more centralized
|
|
||||||
export const UnpackedPermissionSchema = z.object({
|
|
||||||
subject: z.union([z.string().min(1), z.string().array()]).optional(),
|
|
||||||
action: z.union([z.string().min(1), z.string().array()]),
|
|
||||||
conditions: z
|
|
||||||
.object({
|
|
||||||
environment: z.string().optional(),
|
|
||||||
secretPath: z
|
|
||||||
.object({
|
|
||||||
$glob: z.string().min(1)
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
})
|
|
||||||
.optional()
|
|
||||||
});
|
|
||||||
|
|
||||||
const unpackPermissions = (permissions: unknown) =>
|
|
||||||
UnpackedPermissionSchema.array().parse(
|
|
||||||
unpackRules((permissions || []) as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[])
|
|
||||||
);
|
|
||||||
|
|
||||||
export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
||||||
identityProjectAdditionalPrivilegeDAL,
|
identityProjectAdditionalPrivilegeDAL,
|
||||||
identityProjectDAL,
|
identityProjectDAL,
|
||||||
@ -109,10 +86,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
slug,
|
slug,
|
||||||
permissions: customPermission
|
permissions: customPermission
|
||||||
});
|
});
|
||||||
return {
|
return additionalPrivilege;
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
|
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
|
||||||
@ -126,10 +100,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
|
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
|
||||||
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
|
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
|
||||||
});
|
});
|
||||||
return {
|
return additionalPrivilege;
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateBySlug = async ({
|
const updateBySlug = async ({
|
||||||
@ -192,11 +163,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
|
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
|
||||||
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
|
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
|
||||||
});
|
});
|
||||||
return {
|
return additionalPrivilege;
|
||||||
...additionalPrivilege,
|
|
||||||
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
|
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
|
||||||
@ -207,11 +174,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
temporaryRange: null,
|
temporaryRange: null,
|
||||||
temporaryMode: null
|
temporaryMode: null
|
||||||
});
|
});
|
||||||
return {
|
return additionalPrivilege;
|
||||||
...additionalPrivilege,
|
|
||||||
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteBySlug = async ({
|
const deleteBySlug = async ({
|
||||||
@ -257,11 +220,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
||||||
|
|
||||||
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
|
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
|
||||||
return {
|
return deletedPrivilege;
|
||||||
...deletedPrivilege,
|
|
||||||
|
|
||||||
permissions: unpackPermissions(deletedPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPrivilegeDetailsBySlug = async ({
|
const getPrivilegeDetailsBySlug = async ({
|
||||||
@ -295,10 +254,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
});
|
});
|
||||||
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
||||||
|
|
||||||
return {
|
return identityPrivilege;
|
||||||
...identityPrivilege,
|
|
||||||
permissions: unpackPermissions(identityPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const listIdentityProjectPrivileges = async ({
|
const listIdentityProjectPrivileges = async ({
|
||||||
@ -328,11 +284,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find({
|
const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find({
|
||||||
projectMembershipId: identityProjectMembership.id
|
projectMembershipId: identityProjectMembership.id
|
||||||
});
|
});
|
||||||
return identityPrivileges.map((el) => ({
|
return identityPrivileges;
|
||||||
...el,
|
|
||||||
|
|
||||||
permissions: unpackPermissions(el.permissions)
|
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -1,14 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
|
|
||||||
import {
|
import { OrgMembershipRole, OrgMembershipStatus, SecretKeyEncoding, TLdapConfigsUpdate } from "@app/db/schemas";
|
||||||
OrgMembershipRole,
|
|
||||||
OrgMembershipStatus,
|
|
||||||
SecretKeyEncoding,
|
|
||||||
TableName,
|
|
||||||
TLdapConfigsUpdate,
|
|
||||||
TUsers
|
|
||||||
} from "@app/db/schemas";
|
|
||||||
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
||||||
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
|
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
|
||||||
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
||||||
@ -26,15 +19,12 @@ import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
|||||||
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
||||||
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
|
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
||||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
import { normalizeUsername } from "@app/services/user/user-fns";
|
import { normalizeUsername } from "@app/services/user/user-fns";
|
||||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
||||||
import { UserAliasType } from "@app/services/user-alias/user-alias-types";
|
|
||||||
|
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
@ -56,7 +46,6 @@ import { TLdapGroupMapDALFactory } from "./ldap-group-map-dal";
|
|||||||
type TLdapConfigServiceFactoryDep = {
|
type TLdapConfigServiceFactoryDep = {
|
||||||
ldapConfigDAL: Pick<TLdapConfigDALFactory, "create" | "update" | "findOne">;
|
ldapConfigDAL: Pick<TLdapConfigDALFactory, "create" | "update" | "findOne">;
|
||||||
ldapGroupMapDAL: Pick<TLdapGroupMapDALFactory, "find" | "create" | "delete" | "findLdapGroupMapsByLdapConfigId">;
|
ldapGroupMapDAL: Pick<TLdapGroupMapDALFactory, "find" | "create" | "delete" | "findLdapGroupMapsByLdapConfigId">;
|
||||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">;
|
|
||||||
orgDAL: Pick<
|
orgDAL: Pick<
|
||||||
TOrgDALFactory,
|
TOrgDALFactory,
|
||||||
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
|
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
|
||||||
@ -86,7 +75,6 @@ export const ldapConfigServiceFactory = ({
|
|||||||
ldapConfigDAL,
|
ldapConfigDAL,
|
||||||
ldapGroupMapDAL,
|
ldapGroupMapDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
orgBotDAL,
|
orgBotDAL,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
groupProjectDAL,
|
groupProjectDAL,
|
||||||
@ -391,17 +379,16 @@ export const ldapConfigServiceFactory = ({
|
|||||||
username,
|
username,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
email,
|
emails,
|
||||||
groups,
|
groups,
|
||||||
orgId,
|
orgId,
|
||||||
relayState
|
relayState
|
||||||
}: TLdapLoginDTO) => {
|
}: TLdapLoginDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const serverCfg = await getServerCfg();
|
|
||||||
let userAlias = await userAliasDAL.findOne({
|
let userAlias = await userAliasDAL.findOne({
|
||||||
externalId,
|
externalId,
|
||||||
orgId,
|
orgId,
|
||||||
aliasType: UserAliasType.LDAP
|
aliasType: AuthMethod.LDAP
|
||||||
});
|
});
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
@ -409,13 +396,7 @@ export const ldapConfigServiceFactory = ({
|
|||||||
|
|
||||||
if (userAlias) {
|
if (userAlias) {
|
||||||
await userDAL.transaction(async (tx) => {
|
await userDAL.transaction(async (tx) => {
|
||||||
const [orgMembership] = await orgDAL.findMembership(
|
const [orgMembership] = await orgDAL.findMembership({ userId: userAlias.userId }, { tx });
|
||||||
{
|
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: userAlias.userId,
|
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
|
||||||
},
|
|
||||||
{ tx }
|
|
||||||
);
|
|
||||||
if (!orgMembership) {
|
if (!orgMembership) {
|
||||||
await orgDAL.createMembership(
|
await orgDAL.createMembership(
|
||||||
{
|
{
|
||||||
@ -438,75 +419,40 @@ export const ldapConfigServiceFactory = ({
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
userAlias = await userDAL.transaction(async (tx) => {
|
userAlias = await userDAL.transaction(async (tx) => {
|
||||||
let newUser: TUsers | undefined;
|
const uniqueUsername = await normalizeUsername(username, userDAL);
|
||||||
if (serverCfg.trustSamlEmails) {
|
const newUser = await userDAL.create(
|
||||||
newUser = await userDAL.findOne(
|
{
|
||||||
{
|
username: uniqueUsername,
|
||||||
email,
|
email: emails[0],
|
||||||
isEmailVerified: true
|
firstName,
|
||||||
},
|
lastName,
|
||||||
tx
|
authMethods: [AuthMethod.LDAP],
|
||||||
);
|
isGhost: false
|
||||||
}
|
},
|
||||||
|
tx
|
||||||
if (!newUser) {
|
);
|
||||||
const uniqueUsername = await normalizeUsername(username, userDAL);
|
|
||||||
newUser = await userDAL.create(
|
|
||||||
{
|
|
||||||
username: serverCfg.trustLdapEmails ? email : uniqueUsername,
|
|
||||||
email,
|
|
||||||
isEmailVerified: serverCfg.trustLdapEmails,
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
authMethods: [],
|
|
||||||
isGhost: false
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const newUserAlias = await userAliasDAL.create(
|
const newUserAlias = await userAliasDAL.create(
|
||||||
{
|
{
|
||||||
userId: newUser.id,
|
userId: newUser.id,
|
||||||
username,
|
username,
|
||||||
aliasType: UserAliasType.LDAP,
|
aliasType: AuthMethod.LDAP,
|
||||||
externalId,
|
externalId,
|
||||||
emails: [email],
|
emails,
|
||||||
orgId
|
orgId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
const [orgMembership] = await orgDAL.findMembership(
|
await orgDAL.createMembership(
|
||||||
{
|
{
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: newUser.id,
|
userId: newUser.id,
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
orgId,
|
||||||
|
role: OrgMembershipRole.Member,
|
||||||
|
status: OrgMembershipStatus.Invited
|
||||||
},
|
},
|
||||||
{ tx }
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!orgMembership) {
|
|
||||||
await orgMembershipDAL.create(
|
|
||||||
{
|
|
||||||
userId: userAlias.userId,
|
|
||||||
inviteEmail: email,
|
|
||||||
orgId,
|
|
||||||
role: OrgMembershipRole.Member,
|
|
||||||
status: newUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
// Only update the membership to Accepted if the user account is already completed.
|
|
||||||
} else if (orgMembership.status === OrgMembershipStatus.Invited && newUser.isAccepted) {
|
|
||||||
await orgDAL.updateMembershipById(
|
|
||||||
orgMembership.id,
|
|
||||||
{
|
|
||||||
status: OrgMembershipStatus.Accepted
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newUserAlias;
|
return newUserAlias;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -597,14 +543,11 @@ export const ldapConfigServiceFactory = ({
|
|||||||
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
...(user.email && { email: user.email, isEmailVerified: user.isEmailVerified }),
|
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
organizationName: organization.name,
|
organizationName: organization.name,
|
||||||
organizationId: organization.id,
|
organizationId: organization.id,
|
||||||
organizationSlug: organization.slug,
|
|
||||||
authMethod: AuthMethod.LDAP,
|
authMethod: AuthMethod.LDAP,
|
||||||
authType: UserAliasType.LDAP,
|
|
||||||
isUserCompleted,
|
isUserCompleted,
|
||||||
...(relayState
|
...(relayState
|
||||||
? {
|
? {
|
||||||
|
@ -51,7 +51,7 @@ export type TLdapLoginDTO = {
|
|||||||
username: string;
|
username: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
email: string;
|
emails: string[];
|
||||||
orgId: string;
|
orgId: string;
|
||||||
groups?: {
|
groups?: {
|
||||||
dn: string;
|
dn: string;
|
||||||
|
@ -24,8 +24,6 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
|||||||
customAlerts: false,
|
customAlerts: false,
|
||||||
auditLogs: false,
|
auditLogs: false,
|
||||||
auditLogsRetentionDays: 0,
|
auditLogsRetentionDays: 0,
|
||||||
auditLogStreams: false,
|
|
||||||
auditLogStreamLimit: 3,
|
|
||||||
samlSSO: false,
|
samlSSO: false,
|
||||||
scim: false,
|
scim: false,
|
||||||
ldap: false,
|
ldap: false,
|
||||||
|
@ -16,8 +16,6 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
void bd.where({ orgId });
|
void bd.where({ orgId });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
|
||||||
.where(`${TableName.Users}.isGhost`, false)
|
|
||||||
.count();
|
.count();
|
||||||
return doc?.[0].count;
|
return doc?.[0].count;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -121,8 +121,8 @@ export const licenseServiceFactory = ({
|
|||||||
|
|
||||||
if (isValidOfflineLicense) {
|
if (isValidOfflineLicense) {
|
||||||
onPremFeatures = contents.license.features;
|
onPremFeatures = contents.license.features;
|
||||||
instanceType = InstanceType.EnterpriseOnPremOffline;
|
instanceType = InstanceType.EnterpriseOnPrem;
|
||||||
logger.info(`Instance type: ${InstanceType.EnterpriseOnPremOffline}`);
|
logger.info(`Instance type: ${InstanceType.EnterpriseOnPrem}`);
|
||||||
isValidLicense = true;
|
isValidLicense = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import { TOrgPermission } from "@app/lib/types";
|
|||||||
export enum InstanceType {
|
export enum InstanceType {
|
||||||
OnPrem = "self-hosted",
|
OnPrem = "self-hosted",
|
||||||
EnterpriseOnPrem = "enterprise-self-hosted",
|
EnterpriseOnPrem = "enterprise-self-hosted",
|
||||||
EnterpriseOnPremOffline = "enterprise-self-hosted-offline",
|
|
||||||
Cloud = "cloud"
|
Cloud = "cloud"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,8 +40,6 @@ export type TFeatureSet = {
|
|||||||
customAlerts: false;
|
customAlerts: false;
|
||||||
auditLogs: false;
|
auditLogs: false;
|
||||||
auditLogsRetentionDays: 0;
|
auditLogsRetentionDays: 0;
|
||||||
auditLogStreams: false;
|
|
||||||
auditLogStreamLimit: 3;
|
|
||||||
samlSSO: false;
|
samlSSO: false;
|
||||||
scim: false;
|
scim: false;
|
||||||
ldap: false;
|
ldap: false;
|
||||||
|
@ -7,8 +7,7 @@ import {
|
|||||||
SecretKeyEncoding,
|
SecretKeyEncoding,
|
||||||
TableName,
|
TableName,
|
||||||
TSamlConfigs,
|
TSamlConfigs,
|
||||||
TSamlConfigsUpdate,
|
TSamlConfigsUpdate
|
||||||
TUsers
|
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import {
|
import {
|
||||||
@ -20,18 +19,10 @@ import {
|
|||||||
infisicalSymmetricEncypt
|
infisicalSymmetricEncypt
|
||||||
} from "@app/lib/crypto/encryption";
|
} from "@app/lib/crypto/encryption";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { AuthTokenType } from "@app/services/auth/auth-type";
|
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
|
||||||
import { TokenType } from "@app/services/auth-token/auth-token-types";
|
|
||||||
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
|
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
|
||||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
import { normalizeUsername } from "@app/services/user/user-fns";
|
|
||||||
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
|
|
||||||
import { UserAliasType } from "@app/services/user-alias/user-alias-types";
|
|
||||||
|
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
@ -40,19 +31,15 @@ import { TSamlConfigDALFactory } from "./saml-config-dal";
|
|||||||
import { TCreateSamlCfgDTO, TGetSamlCfgDTO, TSamlLoginDTO, TUpdateSamlCfgDTO } from "./saml-config-types";
|
import { TCreateSamlCfgDTO, TGetSamlCfgDTO, TSamlLoginDTO, TUpdateSamlCfgDTO } from "./saml-config-types";
|
||||||
|
|
||||||
type TSamlConfigServiceFactoryDep = {
|
type TSamlConfigServiceFactoryDep = {
|
||||||
samlConfigDAL: Pick<TSamlConfigDALFactory, "create" | "findOne" | "update" | "findById">;
|
samlConfigDAL: TSamlConfigDALFactory;
|
||||||
userDAL: Pick<TUserDALFactory, "create" | "findOne" | "transaction" | "updateById" | "findById">;
|
userDAL: Pick<TUserDALFactory, "create" | "findOne" | "transaction" | "updateById">;
|
||||||
userAliasDAL: Pick<TUserAliasDALFactory, "create" | "findOne">;
|
|
||||||
orgDAL: Pick<
|
orgDAL: Pick<
|
||||||
TOrgDALFactory,
|
TOrgDALFactory,
|
||||||
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
|
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
|
||||||
>;
|
>;
|
||||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">;
|
|
||||||
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "create" | "transaction">;
|
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "create" | "transaction">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
tokenService: Pick<TAuthTokenServiceFactory, "createTokenForUser">;
|
|
||||||
smtpService: Pick<TSmtpService, "sendMail">;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TSamlConfigServiceFactory = ReturnType<typeof samlConfigServiceFactory>;
|
export type TSamlConfigServiceFactory = ReturnType<typeof samlConfigServiceFactory>;
|
||||||
@ -61,13 +48,9 @@ export const samlConfigServiceFactory = ({
|
|||||||
samlConfigDAL,
|
samlConfigDAL,
|
||||||
orgBotDAL,
|
orgBotDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
userDAL,
|
userDAL,
|
||||||
userAliasDAL,
|
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService
|
||||||
tokenService,
|
|
||||||
smtpService
|
|
||||||
}: TSamlConfigServiceFactoryDep) => {
|
}: TSamlConfigServiceFactoryDep) => {
|
||||||
const createSamlCfg = async ({
|
const createSamlCfg = async ({
|
||||||
cert,
|
cert,
|
||||||
@ -322,7 +305,7 @@ export const samlConfigServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const samlLogin = async ({
|
const samlLogin = async ({
|
||||||
externalId,
|
username,
|
||||||
email,
|
email,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
@ -331,40 +314,38 @@ export const samlConfigServiceFactory = ({
|
|||||||
relayState
|
relayState
|
||||||
}: TSamlLoginDTO) => {
|
}: TSamlLoginDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const serverCfg = await getServerCfg();
|
let user = await userDAL.findOne({ username });
|
||||||
const userAlias = await userAliasDAL.findOne({
|
|
||||||
externalId,
|
|
||||||
orgId,
|
|
||||||
aliasType: UserAliasType.SAML
|
|
||||||
});
|
|
||||||
|
|
||||||
const organization = await orgDAL.findOrgById(orgId);
|
const organization = await orgDAL.findOrgById(orgId);
|
||||||
if (!organization) throw new BadRequestError({ message: "Org not found" });
|
if (!organization) throw new BadRequestError({ message: "Org not found" });
|
||||||
|
|
||||||
let user: TUsers;
|
// TODO(dangtony98): remove this after aliases update
|
||||||
if (userAlias) {
|
if (authProvider === AuthMethod.KEYCLOAK_SAML && appCfg.LICENSE_SERVER_KEY) {
|
||||||
user = await userDAL.transaction(async (tx) => {
|
throw new BadRequestError({ message: "Keycloak SAML is not yet available on Infisical Cloud" });
|
||||||
const foundUser = await userDAL.findById(userAlias.userId, tx);
|
}
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
await userDAL.transaction(async (tx) => {
|
||||||
const [orgMembership] = await orgDAL.findMembership(
|
const [orgMembership] = await orgDAL.findMembership(
|
||||||
{
|
{
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: foundUser.id,
|
userId: user.id,
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
);
|
);
|
||||||
if (!orgMembership) {
|
if (!orgMembership) {
|
||||||
await orgMembershipDAL.create(
|
await orgDAL.createMembership(
|
||||||
{
|
{
|
||||||
userId: userAlias.userId,
|
userId: user.id,
|
||||||
inviteEmail: email,
|
|
||||||
orgId,
|
orgId,
|
||||||
|
inviteEmail: email,
|
||||||
role: OrgMembershipRole.Member,
|
role: OrgMembershipRole.Member,
|
||||||
status: foundUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
|
status: user.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
// Only update the membership to Accepted if the user account is already completed.
|
// Only update the membership to Accepted if the user account is already completed.
|
||||||
} else if (orgMembership.status === OrgMembershipStatus.Invited && foundUser.isAccepted) {
|
} else if (orgMembership.status === OrgMembershipStatus.Invited && user.isAccepted) {
|
||||||
await orgDAL.updateMembershipById(
|
await orgDAL.updateMembershipById(
|
||||||
orgMembership.id,
|
orgMembership.id,
|
||||||
{
|
{
|
||||||
@ -373,97 +354,40 @@ export const samlConfigServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return foundUser;
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
user = await userDAL.transaction(async (tx) => {
|
user = await userDAL.transaction(async (tx) => {
|
||||||
let newUser: TUsers | undefined;
|
const newUser = await userDAL.create(
|
||||||
if (serverCfg.trustSamlEmails) {
|
|
||||||
newUser = await userDAL.findOne(
|
|
||||||
{
|
|
||||||
email,
|
|
||||||
isEmailVerified: true
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!newUser) {
|
|
||||||
const uniqueUsername = await normalizeUsername(`${firstName ?? ""}-${lastName ?? ""}`, userDAL);
|
|
||||||
newUser = await userDAL.create(
|
|
||||||
{
|
|
||||||
username: serverCfg.trustSamlEmails ? email : uniqueUsername,
|
|
||||||
email,
|
|
||||||
isEmailVerified: serverCfg.trustSamlEmails,
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
authMethods: [],
|
|
||||||
isGhost: false
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await userAliasDAL.create(
|
|
||||||
{
|
{
|
||||||
userId: newUser.id,
|
username,
|
||||||
aliasType: UserAliasType.SAML,
|
email,
|
||||||
externalId,
|
firstName,
|
||||||
emails: email ? [email] : [],
|
lastName,
|
||||||
orgId
|
authMethods: [AuthMethod.EMAIL],
|
||||||
|
isGhost: false
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
await orgDAL.createMembership({
|
||||||
const [orgMembership] = await orgDAL.findMembership(
|
inviteEmail: email,
|
||||||
{
|
orgId,
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: newUser.id,
|
role: OrgMembershipRole.Member,
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
status: OrgMembershipStatus.Invited
|
||||||
},
|
});
|
||||||
{ tx }
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!orgMembership) {
|
|
||||||
await orgMembershipDAL.create(
|
|
||||||
{
|
|
||||||
userId: newUser.id,
|
|
||||||
inviteEmail: email,
|
|
||||||
orgId,
|
|
||||||
role: OrgMembershipRole.Member,
|
|
||||||
status: newUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
// Only update the membership to Accepted if the user account is already completed.
|
|
||||||
} else if (orgMembership.status === OrgMembershipStatus.Invited && newUser.isAccepted) {
|
|
||||||
await orgDAL.updateMembershipById(
|
|
||||||
orgMembership.id,
|
|
||||||
{
|
|
||||||
status: OrgMembershipStatus.Accepted
|
|
||||||
},
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newUser;
|
return newUser;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const isUserCompleted = Boolean(user.isAccepted);
|
const isUserCompleted = Boolean(user.isAccepted);
|
||||||
const providerAuthToken = jwt.sign(
|
const providerAuthToken = jwt.sign(
|
||||||
{
|
{
|
||||||
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
authTokenType: AuthTokenType.PROVIDER_TOKEN,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
...(user.email && { email: user.email, isEmailVerified: user.isEmailVerified }),
|
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
organizationName: organization.name,
|
organizationName: organization.name,
|
||||||
organizationId: organization.id,
|
organizationId: organization.id,
|
||||||
organizationSlug: organization.slug,
|
|
||||||
authMethod: authProvider,
|
authMethod: authProvider,
|
||||||
authType: UserAliasType.SAML,
|
|
||||||
isUserCompleted,
|
isUserCompleted,
|
||||||
...(relayState
|
...(relayState
|
||||||
? {
|
? {
|
||||||
@ -479,22 +403,6 @@ export const samlConfigServiceFactory = ({
|
|||||||
|
|
||||||
await samlConfigDAL.update({ orgId }, { lastUsed: new Date() });
|
await samlConfigDAL.update({ orgId }, { lastUsed: new Date() });
|
||||||
|
|
||||||
if (user.email && !user.isEmailVerified) {
|
|
||||||
const token = await tokenService.createTokenForUser({
|
|
||||||
type: TokenType.TOKEN_EMAIL_VERIFICATION,
|
|
||||||
userId: user.id
|
|
||||||
});
|
|
||||||
|
|
||||||
await smtpService.sendMail({
|
|
||||||
template: SmtpTemplates.EmailVerification,
|
|
||||||
subjectLine: "Infisical confirmation code",
|
|
||||||
recipients: [user.email],
|
|
||||||
substitutions: {
|
|
||||||
code: token
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return { isUserCompleted, providerAuthToken };
|
return { isUserCompleted, providerAuthToken };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,8 +45,8 @@ export type TGetSamlCfgDTO =
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TSamlLoginDTO = {
|
export type TSamlLoginDTO = {
|
||||||
externalId: string;
|
username: string;
|
||||||
email: string;
|
email?: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName?: string;
|
lastName?: string;
|
||||||
authProvider: string;
|
authProvider: string;
|
||||||
|
@ -2,31 +2,31 @@ import { TListScimGroups, TListScimUsers, TScimGroup, TScimUser } from "./scim-t
|
|||||||
|
|
||||||
export const buildScimUserList = ({
|
export const buildScimUserList = ({
|
||||||
scimUsers,
|
scimUsers,
|
||||||
startIndex,
|
offset,
|
||||||
limit
|
limit
|
||||||
}: {
|
}: {
|
||||||
scimUsers: TScimUser[];
|
scimUsers: TScimUser[];
|
||||||
startIndex: number;
|
offset: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
}): TListScimUsers => {
|
}): TListScimUsers => {
|
||||||
return {
|
return {
|
||||||
Resources: scimUsers,
|
Resources: scimUsers,
|
||||||
itemsPerPage: limit,
|
itemsPerPage: limit,
|
||||||
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
|
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
|
||||||
startIndex,
|
startIndex: offset,
|
||||||
totalResults: scimUsers.length
|
totalResults: scimUsers.length
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const buildScimUser = ({
|
export const buildScimUser = ({
|
||||||
orgMembershipId,
|
userId,
|
||||||
username,
|
username,
|
||||||
email,
|
email,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
active
|
active
|
||||||
}: {
|
}: {
|
||||||
orgMembershipId: string;
|
userId: string;
|
||||||
username: string;
|
username: string;
|
||||||
email?: string | null;
|
email?: string | null;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
@ -35,7 +35,7 @@ export const buildScimUser = ({
|
|||||||
}): TScimUser => {
|
}): TScimUser => {
|
||||||
const scimUser = {
|
const scimUser = {
|
||||||
schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
||||||
id: orgMembershipId,
|
id: userId,
|
||||||
userName: username,
|
userName: username,
|
||||||
displayName: `${firstName} ${lastName}`,
|
displayName: `${firstName} ${lastName}`,
|
||||||
name: {
|
name: {
|
||||||
@ -65,18 +65,18 @@ export const buildScimUser = ({
|
|||||||
|
|
||||||
export const buildScimGroupList = ({
|
export const buildScimGroupList = ({
|
||||||
scimGroups,
|
scimGroups,
|
||||||
startIndex,
|
offset,
|
||||||
limit
|
limit
|
||||||
}: {
|
}: {
|
||||||
scimGroups: TScimGroup[];
|
scimGroups: TScimGroup[];
|
||||||
startIndex: number;
|
offset: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
}): TListScimGroups => {
|
}): TListScimGroups => {
|
||||||
return {
|
return {
|
||||||
Resources: scimGroups,
|
Resources: scimGroups,
|
||||||
itemsPerPage: limit,
|
itemsPerPage: limit,
|
||||||
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
|
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
|
||||||
startIndex,
|
startIndex: offset,
|
||||||
totalResults: scimGroups.length
|
totalResults: scimGroups.length
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user