mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-07 22:53:55 -05:00
merge main
This commit is contained in:
4
.github/workflows/check-fe-ts-and-lint.yml
vendored
4
.github/workflows/check-fe-ts-and-lint.yml
vendored
@@ -30,6 +30,6 @@ jobs:
|
|||||||
- name: 🏗️ Run Type check
|
- name: 🏗️ Run Type check
|
||||||
run: npm run type:check
|
run: npm run type:check
|
||||||
working-directory: frontend
|
working-directory: frontend
|
||||||
- name: 🏗️ Run Link check
|
- name: 🏗️ Run Lint check
|
||||||
run: npm run lint:fix
|
run: npm run lint
|
||||||
working-directory: frontend
|
working-directory: frontend
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Set up chart-testing
|
- name: Set up chart-testing
|
||||||
uses: helm/chart-testing-action@v2.7.0
|
uses: helm/chart-testing-action@v2.7.0
|
||||||
|
with:
|
||||||
|
yamale_version: "6.0.0"
|
||||||
|
|
||||||
- name: Run chart-testing (lint)
|
- name: Run chart-testing (lint)
|
||||||
run: ct lint --config ct.yaml --charts helm-charts/infisical-standalone-postgres
|
run: ct lint --config ct.yaml --charts helm-charts/infisical-standalone-postgres
|
||||||
|
|||||||
2
.github/workflows/nightly-tag-generation.yml
vendored
2
.github/workflows/nightly-tag-generation.yml
vendored
@@ -1,8 +1,6 @@
|
|||||||
name: Generate Nightly Tag
|
name: Generate Nightly Tag
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
|
||||||
- cron: '0 0 * * *' # Run daily at midnight UTC
|
|
||||||
workflow_dispatch: # Allow manual triggering for testing
|
workflow_dispatch: # Allow manual triggering for testing
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
@@ -65,6 +65,15 @@ jobs:
|
|||||||
INFISICAL_PLATFORM_VERSION=${{ steps.extract_version.outputs.version }}
|
INFISICAL_PLATFORM_VERSION=${{ steps.extract_version.outputs.version }}
|
||||||
DD_GIT_REPOSITORY_URL=${{ github.server_url }}/${{ github.repository }}
|
DD_GIT_REPOSITORY_URL=${{ github.server_url }}/${{ github.repository }}
|
||||||
DD_GIT_COMMIT_SHA=${{ github.sha }}
|
DD_GIT_COMMIT_SHA=${{ github.sha }}
|
||||||
|
- name: Snyk to check Docker image for vulnerabilities
|
||||||
|
continue-on-error: true
|
||||||
|
uses: snyk/actions/docker@master
|
||||||
|
env:
|
||||||
|
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||||
|
with:
|
||||||
|
image: infisical/infisical:${{ steps.extract_version.outputs.version }}
|
||||||
|
command: monitor
|
||||||
|
args: --file=Dockerfile.standalone-infisical --project-name="infisical-core-docker-image"
|
||||||
|
|
||||||
infisical-fips-standalone:
|
infisical-fips-standalone:
|
||||||
name: Build infisical standalone image postgres
|
name: Build infisical standalone image postgres
|
||||||
@@ -141,4 +150,4 @@ jobs:
|
|||||||
echo "Successfully created tag $TAG_NAME"
|
echo "Successfully created tag $TAG_NAME"
|
||||||
fi
|
fi
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.OMNIBUS_RELEASE_TOKEN }}
|
GH_TOKEN: ${{ secrets.OMNIBUS_RELEASE_TOKEN }}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Set up chart-testing
|
- name: Set up chart-testing
|
||||||
uses: helm/chart-testing-action@v2.7.0
|
uses: helm/chart-testing-action@v2.7.0
|
||||||
|
with:
|
||||||
|
yamale_version: "6.0.0"
|
||||||
|
|
||||||
- name: Run chart-testing (lint)
|
- name: Run chart-testing (lint)
|
||||||
run: ct lint --config ct.yaml --charts helm-charts/infisical-standalone-postgres
|
run: ct lint --config ct.yaml --charts helm-charts/infisical-standalone-postgres
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ ARG POSTHOG_API_KEY=posthog-api-key
|
|||||||
ARG INTERCOM_ID=intercom-id
|
ARG INTERCOM_ID=intercom-id
|
||||||
ARG CAPTCHA_SITE_KEY=captcha-site-key
|
ARG CAPTCHA_SITE_KEY=captcha-site-key
|
||||||
|
|
||||||
FROM node:20-slim AS base
|
FROM node:20.19.5-trixie-slim AS base
|
||||||
|
|
||||||
|
# Fixes NPM vulnerability: https://security.snyk.io/vuln/SNYK-JS-CROSSSPAWN-8303230
|
||||||
|
RUN npm install -g npm@11
|
||||||
|
|
||||||
FROM base AS frontend-dependencies
|
FROM base AS frontend-dependencies
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@@ -155,7 +158,7 @@ RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
|
|||||||
|
|
||||||
# Install Infisical CLI
|
# Install Infisical CLI
|
||||||
RUN curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash \
|
RUN curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash \
|
||||||
&& apt-get update && apt-get install -y infisical=0.41.89 \
|
&& apt-get update && apt-get install -y infisical=0.42.6 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN groupadd -r -g 1001 nodejs && useradd -r -u 1001 -g nodejs non-root-user
|
RUN groupadd -r -g 1001 nodejs && useradd -r -u 1001 -g nodejs non-root-user
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ ARG POSTHOG_API_KEY=posthog-api-key
|
|||||||
ARG INTERCOM_ID=intercom-id
|
ARG INTERCOM_ID=intercom-id
|
||||||
ARG CAPTCHA_SITE_KEY=captcha-site-key
|
ARG CAPTCHA_SITE_KEY=captcha-site-key
|
||||||
|
|
||||||
FROM node:20-slim AS base
|
FROM node:20.19.5-trixie-slim AS base
|
||||||
|
|
||||||
|
# Fixes NPM vulnerability: https://security.snyk.io/vuln/SNYK-JS-CROSSSPAWN-8303230
|
||||||
|
RUN npm install -g npm@11
|
||||||
|
|
||||||
FROM base AS frontend-dependencies
|
FROM base AS frontend-dependencies
|
||||||
|
|
||||||
@@ -139,7 +142,7 @@ RUN apt-get update && apt-get install -y \
|
|||||||
|
|
||||||
# Install Infisical CLI
|
# Install Infisical CLI
|
||||||
RUN curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash \
|
RUN curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash \
|
||||||
&& apt-get update && apt-get install -y infisical=0.41.89 \
|
&& apt-get update && apt-get install -y infisical=0.42.6 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build stage
|
# Build stage
|
||||||
FROM node:20-slim AS build
|
FROM node:20.19.5-trixie-slim AS build
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ COPY . .
|
|||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# Production stage
|
# Production stage
|
||||||
FROM node:20-slim
|
FROM node:20.19.5-trixie-slim
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
ENV npm_config_cache /home/node/.npm
|
ENV npm_config_cache /home/node/.npm
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:20-slim
|
FROM node:20.19.5-trixie-slim
|
||||||
|
|
||||||
# ? Setup a test SoftHSM module. In production a real HSM is used.
|
# ? Setup a test SoftHSM module. In production a real HSM is used.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:20-slim
|
FROM node:20.19.5-trixie-slim
|
||||||
|
|
||||||
# ? Setup a test SoftHSM module. In production a real HSM is used.
|
# ? Setup a test SoftHSM module. In production a real HSM is used.
|
||||||
|
|
||||||
|
|||||||
4874
backend/package-lock.json
generated
4874
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -79,12 +79,17 @@
|
|||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
"overrides": {
|
||||||
|
"cipher-base": "1.0.5",
|
||||||
|
"sha.js": "2.4.12"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.18.10",
|
"@babel/cli": "^7.18.10",
|
||||||
"@babel/core": "^7.18.10",
|
"@babel/core": "^7.18.10",
|
||||||
"@babel/plugin-syntax-import-attributes": "^7.24.7",
|
"@babel/plugin-syntax-import-attributes": "^7.24.7",
|
||||||
"@babel/preset-env": "^7.18.10",
|
"@babel/preset-env": "^7.18.10",
|
||||||
"@babel/preset-react": "^7.24.7",
|
"@babel/preset-react": "^7.24.7",
|
||||||
|
"@react-email/preview-server": "^4.3.0",
|
||||||
"@smithy/types": "^4.3.1",
|
"@smithy/types": "^4.3.1",
|
||||||
"@types/bcrypt": "^5.0.2",
|
"@types/bcrypt": "^5.0.2",
|
||||||
"@types/jmespath": "^0.15.2",
|
"@types/jmespath": "^0.15.2",
|
||||||
@@ -120,7 +125,7 @@
|
|||||||
"nodemon": "^3.0.2",
|
"nodemon": "^3.0.2",
|
||||||
"pino-pretty": "^10.2.3",
|
"pino-pretty": "^10.2.3",
|
||||||
"prompt-sync": "^4.2.0",
|
"prompt-sync": "^4.2.0",
|
||||||
"react-email": "4.0.7",
|
"react-email": "^4.3.0",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tsc-alias": "^1.8.8",
|
"tsc-alias": "^1.8.8",
|
||||||
@@ -138,7 +143,7 @@
|
|||||||
"@aws-sdk/client-secrets-manager": "^3.504.0",
|
"@aws-sdk/client-secrets-manager": "^3.504.0",
|
||||||
"@aws-sdk/client-sts": "^3.600.0",
|
"@aws-sdk/client-sts": "^3.600.0",
|
||||||
"@casl/ability": "^6.5.0",
|
"@casl/ability": "^6.5.0",
|
||||||
"@elastic/elasticsearch": "^8.15.0",
|
"@elastic/elasticsearch": "^9.1.1",
|
||||||
"@fastify/cookie": "^9.3.1",
|
"@fastify/cookie": "^9.3.1",
|
||||||
"@fastify/cors": "^8.5.0",
|
"@fastify/cors": "^8.5.0",
|
||||||
"@fastify/etag": "^5.1.0",
|
"@fastify/etag": "^5.1.0",
|
||||||
@@ -185,7 +190,7 @@
|
|||||||
"ajv": "^8.12.0",
|
"ajv": "^8.12.0",
|
||||||
"argon2": "^0.31.2",
|
"argon2": "^0.31.2",
|
||||||
"aws-sdk": "^2.1553.0",
|
"aws-sdk": "^2.1553.0",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.12.0",
|
||||||
"axios-ntlm": "^1.4.4",
|
"axios-ntlm": "^1.4.4",
|
||||||
"axios-retry": "^4.0.0",
|
"axios-retry": "^4.0.0",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
@@ -196,7 +201,7 @@
|
|||||||
"cron": "^3.1.7",
|
"cron": "^3.1.7",
|
||||||
"dd-trace": "^5.40.0",
|
"dd-trace": "^5.40.0",
|
||||||
"dotenv": "^16.4.1",
|
"dotenv": "^16.4.1",
|
||||||
"fastify": "^4.28.1",
|
"fastify": "^4.29.1",
|
||||||
"fastify-plugin": "^4.5.1",
|
"fastify-plugin": "^4.5.1",
|
||||||
"google-auth-library": "^9.9.0",
|
"google-auth-library": "^9.9.0",
|
||||||
"googleapis": "^137.1.0",
|
"googleapis": "^137.1.0",
|
||||||
|
|||||||
@@ -85,10 +85,9 @@ const getZodDefaultValue = (type: unknown, value: string | number | boolean | Ob
|
|||||||
};
|
};
|
||||||
|
|
||||||
const bigIntegerColumns: Record<string, string[]> = {
|
const bigIntegerColumns: Record<string, string[]> = {
|
||||||
"folder_commits": ["commitId"]
|
folder_commits: ["commitId"]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
const tables = (
|
const tables = (
|
||||||
await db("information_schema.tables")
|
await db("information_schema.tables")
|
||||||
@@ -99,6 +98,7 @@ const main = async () => {
|
|||||||
(el) =>
|
(el) =>
|
||||||
!el.tableName.includes("_migrations") &&
|
!el.tableName.includes("_migrations") &&
|
||||||
!el.tableName.includes("audit_logs_") &&
|
!el.tableName.includes("audit_logs_") &&
|
||||||
|
!el.tableName.includes("user_notifications_") &&
|
||||||
!el.tableName.includes("active_locks") &&
|
!el.tableName.includes("active_locks") &&
|
||||||
el.tableName !== "intermediate_audit_logs"
|
el.tableName !== "intermediate_audit_logs"
|
||||||
);
|
);
|
||||||
|
|||||||
23
backend/src/@types/fastify.d.ts
vendored
23
backend/src/@types/fastify.d.ts
vendored
@@ -20,8 +20,6 @@ import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2
|
|||||||
import { TGithubOrgSyncServiceFactory } from "@app/ee/services/github-org-sync/github-org-sync-service";
|
import { TGithubOrgSyncServiceFactory } from "@app/ee/services/github-org-sync/github-org-sync-service";
|
||||||
import { TGroupServiceFactory } from "@app/ee/services/group/group-service";
|
import { TGroupServiceFactory } from "@app/ee/services/group/group-service";
|
||||||
import { TIdentityAuthTemplateServiceFactory } from "@app/ee/services/identity-auth-template";
|
import { TIdentityAuthTemplateServiceFactory } from "@app/ee/services/identity-auth-template";
|
||||||
import { TIdentityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service";
|
|
||||||
import { TIdentityProjectAdditionalPrivilegeV2ServiceFactory } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-service";
|
|
||||||
import { TKmipClientDALFactory } from "@app/ee/services/kmip/kmip-client-dal";
|
import { TKmipClientDALFactory } from "@app/ee/services/kmip/kmip-client-dal";
|
||||||
import { TKmipOperationServiceFactory } from "@app/ee/services/kmip/kmip-operation-service";
|
import { TKmipOperationServiceFactory } from "@app/ee/services/kmip/kmip-operation-service";
|
||||||
import { TKmipServiceFactory } from "@app/ee/services/kmip/kmip-service";
|
import { TKmipServiceFactory } from "@app/ee/services/kmip/kmip-service";
|
||||||
@@ -35,7 +33,6 @@ import { TPamSessionServiceFactory } from "@app/ee/services/pam-session/pam-sess
|
|||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { TPitServiceFactory } from "@app/ee/services/pit/pit-service";
|
import { TPitServiceFactory } from "@app/ee/services/pit/pit-service";
|
||||||
import { TProjectTemplateServiceFactory } from "@app/ee/services/project-template/project-template-types";
|
import { TProjectTemplateServiceFactory } from "@app/ee/services/project-template/project-template-types";
|
||||||
import { TProjectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types";
|
|
||||||
import { RateLimitConfiguration, TRateLimitServiceFactory } from "@app/ee/services/rate-limit/rate-limit-types";
|
import { RateLimitConfiguration, TRateLimitServiceFactory } from "@app/ee/services/rate-limit/rate-limit-types";
|
||||||
import { TRelayServiceFactory } from "@app/ee/services/relay/relay-service";
|
import { TRelayServiceFactory } from "@app/ee/services/relay/relay-service";
|
||||||
import { TSamlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-types";
|
import { TSamlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-types";
|
||||||
@@ -53,6 +50,7 @@ import { TSshHostServiceFactory } from "@app/ee/services/ssh-host/ssh-host-servi
|
|||||||
import { TSshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service";
|
import { TSshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service";
|
||||||
import { TTrustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-types";
|
import { TTrustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-types";
|
||||||
import { TAuthMode } from "@app/server/plugins/auth/inject-identity";
|
import { TAuthMode } from "@app/server/plugins/auth/inject-identity";
|
||||||
|
import { TAdditionalPrivilegeServiceFactory } from "@app/services/additional-privilege/additional-privilege-service";
|
||||||
import { TApiKeyServiceFactory } from "@app/services/api-key/api-key-service";
|
import { TApiKeyServiceFactory } from "@app/services/api-key/api-key-service";
|
||||||
import { TAppConnectionServiceFactory } from "@app/services/app-connection/app-connection-service";
|
import { TAppConnectionServiceFactory } from "@app/services/app-connection/app-connection-service";
|
||||||
import { TAuthLoginFactory } from "@app/services/auth/auth-login-service";
|
import { TAuthLoginFactory } from "@app/services/auth/auth-login-service";
|
||||||
@@ -65,6 +63,7 @@ import { TCertificateAuthorityServiceFactory } from "@app/services/certificate-a
|
|||||||
import { TInternalCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/internal/internal-certificate-authority-service";
|
import { TInternalCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/internal/internal-certificate-authority-service";
|
||||||
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
||||||
import { TCmekServiceFactory } from "@app/services/cmek/cmek-service";
|
import { TCmekServiceFactory } from "@app/services/cmek/cmek-service";
|
||||||
|
import { TConvertorServiceFactory } from "@app/services/convertor/convertor-service";
|
||||||
import { TExternalGroupOrgRoleMappingServiceFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-service";
|
import { TExternalGroupOrgRoleMappingServiceFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-service";
|
||||||
import { TExternalMigrationServiceFactory } from "@app/services/external-migration/external-migration-service";
|
import { TExternalMigrationServiceFactory } from "@app/services/external-migration/external-migration-service";
|
||||||
import { TFolderCommitServiceFactory } from "@app/services/folder-commit/folder-commit-service";
|
import { TFolderCommitServiceFactory } from "@app/services/folder-commit/folder-commit-service";
|
||||||
@@ -88,10 +87,12 @@ import { TIdentityTokenAuthServiceFactory } from "@app/services/identity-token-a
|
|||||||
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";
|
||||||
import { TIntegrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service";
|
import { TIntegrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service";
|
||||||
|
import { TMembershipGroupServiceFactory } from "@app/services/membership-group/membership-group-service";
|
||||||
|
import { TMembershipIdentityServiceFactory } from "@app/services/membership-identity/membership-identity-service";
|
||||||
|
import { TMembershipUserServiceFactory } from "@app/services/membership-user/membership-user-service";
|
||||||
import { TMicrosoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service";
|
import { TMicrosoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service";
|
||||||
import { TNotificationServiceFactory } from "@app/services/notification/notification-service";
|
import { TNotificationServiceFactory } from "@app/services/notification/notification-service";
|
||||||
import { TOfflineUsageReportServiceFactory } from "@app/services/offline-usage-report/offline-usage-report-service";
|
import { TOfflineUsageReportServiceFactory } from "@app/services/offline-usage-report/offline-usage-report-service";
|
||||||
import { TOrgRoleServiceFactory } from "@app/services/org/org-role-service";
|
|
||||||
import { TOrgServiceFactory } from "@app/services/org/org-service";
|
import { TOrgServiceFactory } from "@app/services/org/org-service";
|
||||||
import { TOrgAdminServiceFactory } from "@app/services/org-admin/org-admin-service";
|
import { TOrgAdminServiceFactory } from "@app/services/org-admin/org-admin-service";
|
||||||
import { TPkiAlertServiceFactory } from "@app/services/pki-alert/pki-alert-service";
|
import { TPkiAlertServiceFactory } from "@app/services/pki-alert/pki-alert-service";
|
||||||
@@ -104,8 +105,8 @@ import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot
|
|||||||
import { TProjectEnvServiceFactory } from "@app/services/project-env/project-env-service";
|
import { TProjectEnvServiceFactory } from "@app/services/project-env/project-env-service";
|
||||||
import { TProjectKeyServiceFactory } from "@app/services/project-key/project-key-service";
|
import { TProjectKeyServiceFactory } from "@app/services/project-key/project-key-service";
|
||||||
import { TProjectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service";
|
import { TProjectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service";
|
||||||
import { TProjectRoleServiceFactory } from "@app/services/project-role/project-role-service";
|
|
||||||
import { TReminderServiceFactory } from "@app/services/reminder/reminder-types";
|
import { TReminderServiceFactory } from "@app/services/reminder/reminder-types";
|
||||||
|
import { TRoleServiceFactory } from "@app/services/role/role-service";
|
||||||
import { TSecretServiceFactory } from "@app/services/secret/secret-service";
|
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";
|
||||||
@@ -213,7 +214,6 @@ declare module "fastify" {
|
|||||||
authToken: TAuthTokenServiceFactory;
|
authToken: TAuthTokenServiceFactory;
|
||||||
permission: TPermissionServiceFactory;
|
permission: TPermissionServiceFactory;
|
||||||
org: TOrgServiceFactory;
|
org: TOrgServiceFactory;
|
||||||
orgRole: TOrgRoleServiceFactory;
|
|
||||||
oidc: TOidcConfigServiceFactory;
|
oidc: TOidcConfigServiceFactory;
|
||||||
superAdmin: TSuperAdminServiceFactory;
|
superAdmin: TSuperAdminServiceFactory;
|
||||||
user: TUserServiceFactory;
|
user: TUserServiceFactory;
|
||||||
@@ -225,7 +225,6 @@ declare module "fastify" {
|
|||||||
projectMembership: TProjectMembershipServiceFactory;
|
projectMembership: TProjectMembershipServiceFactory;
|
||||||
projectEnv: TProjectEnvServiceFactory;
|
projectEnv: TProjectEnvServiceFactory;
|
||||||
projectKey: TProjectKeyServiceFactory;
|
projectKey: TProjectKeyServiceFactory;
|
||||||
projectRole: TProjectRoleServiceFactory;
|
|
||||||
secret: TSecretServiceFactory;
|
secret: TSecretServiceFactory;
|
||||||
secretReplication: TSecretReplicationServiceFactory;
|
secretReplication: TSecretReplicationServiceFactory;
|
||||||
secretTag: TSecretTagServiceFactory;
|
secretTag: TSecretTagServiceFactory;
|
||||||
@@ -281,9 +280,6 @@ declare module "fastify" {
|
|||||||
telemetry: TTelemetryServiceFactory;
|
telemetry: TTelemetryServiceFactory;
|
||||||
dynamicSecret: TDynamicSecretServiceFactory;
|
dynamicSecret: TDynamicSecretServiceFactory;
|
||||||
dynamicSecretLease: TDynamicSecretLeaseServiceFactory;
|
dynamicSecretLease: TDynamicSecretLeaseServiceFactory;
|
||||||
projectUserAdditionalPrivilege: TProjectUserAdditionalPrivilegeServiceFactory;
|
|
||||||
identityProjectAdditionalPrivilege: TIdentityProjectAdditionalPrivilegeServiceFactory;
|
|
||||||
identityProjectAdditionalPrivilegeV2: TIdentityProjectAdditionalPrivilegeV2ServiceFactory;
|
|
||||||
secretSharing: TSecretSharingServiceFactory;
|
secretSharing: TSecretSharingServiceFactory;
|
||||||
rateLimit: TRateLimitServiceFactory;
|
rateLimit: TRateLimitServiceFactory;
|
||||||
userEngagement: TUserEngagementServiceFactory;
|
userEngagement: TUserEngagementServiceFactory;
|
||||||
@@ -324,6 +320,13 @@ declare module "fastify" {
|
|||||||
pamAccount: TPamAccountServiceFactory;
|
pamAccount: TPamAccountServiceFactory;
|
||||||
pamSession: TPamSessionServiceFactory;
|
pamSession: TPamSessionServiceFactory;
|
||||||
upgradePath: TUpgradePathService;
|
upgradePath: TUpgradePathService;
|
||||||
|
|
||||||
|
membershipUser: TMembershipUserServiceFactory;
|
||||||
|
membershipIdentity: TMembershipIdentityServiceFactory;
|
||||||
|
membershipGroup: TMembershipGroupServiceFactory;
|
||||||
|
additionalPrivilege: TAdditionalPrivilegeServiceFactory;
|
||||||
|
role: TRoleServiceFactory;
|
||||||
|
convertor: TConvertorServiceFactory;
|
||||||
};
|
};
|
||||||
// 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
|
||||||
|
|||||||
29
backend/src/@types/knex.d.ts
vendored
29
backend/src/@types/knex.d.ts
vendored
@@ -17,6 +17,9 @@ import {
|
|||||||
TAccessApprovalRequestsReviewersInsert,
|
TAccessApprovalRequestsReviewersInsert,
|
||||||
TAccessApprovalRequestsReviewersUpdate,
|
TAccessApprovalRequestsReviewersUpdate,
|
||||||
TAccessApprovalRequestsUpdate,
|
TAccessApprovalRequestsUpdate,
|
||||||
|
TAdditionalPrivileges,
|
||||||
|
TAdditionalPrivilegesInsert,
|
||||||
|
TAdditionalPrivilegesUpdate,
|
||||||
TApiKeys,
|
TApiKeys,
|
||||||
TApiKeysInsert,
|
TApiKeysInsert,
|
||||||
TApiKeysUpdate,
|
TApiKeysUpdate,
|
||||||
@@ -227,6 +230,15 @@ import {
|
|||||||
TLdapGroupMaps,
|
TLdapGroupMaps,
|
||||||
TLdapGroupMapsInsert,
|
TLdapGroupMapsInsert,
|
||||||
TLdapGroupMapsUpdate,
|
TLdapGroupMapsUpdate,
|
||||||
|
TMembershipRoles,
|
||||||
|
TMembershipRolesInsert,
|
||||||
|
TMembershipRolesUpdate,
|
||||||
|
TMemberships,
|
||||||
|
TMembershipsInsert,
|
||||||
|
TMembershipsUpdate,
|
||||||
|
TNamespaces,
|
||||||
|
TNamespacesInsert,
|
||||||
|
TNamespacesUpdate,
|
||||||
TOidcConfigs,
|
TOidcConfigs,
|
||||||
TOidcConfigsInsert,
|
TOidcConfigsInsert,
|
||||||
TOidcConfigsUpdate,
|
TOidcConfigsUpdate,
|
||||||
@@ -314,6 +326,9 @@ import {
|
|||||||
TResourceMetadata,
|
TResourceMetadata,
|
||||||
TResourceMetadataInsert,
|
TResourceMetadataInsert,
|
||||||
TResourceMetadataUpdate,
|
TResourceMetadataUpdate,
|
||||||
|
TRoles,
|
||||||
|
TRolesInsert,
|
||||||
|
TRolesUpdate,
|
||||||
TSamlConfigs,
|
TSamlConfigs,
|
||||||
TSamlConfigsInsert,
|
TSamlConfigsInsert,
|
||||||
TSamlConfigsUpdate,
|
TSamlConfigsUpdate,
|
||||||
@@ -1316,5 +1331,19 @@ declare module "knex/types/tables" {
|
|||||||
[TableName.PamResource]: KnexOriginal.CompositeTableType<TPamResources, TPamResourcesInsert, TPamResourcesUpdate>;
|
[TableName.PamResource]: KnexOriginal.CompositeTableType<TPamResources, TPamResourcesInsert, TPamResourcesUpdate>;
|
||||||
[TableName.PamAccount]: KnexOriginal.CompositeTableType<TPamAccounts, TPamAccountsInsert, TPamAccountsUpdate>;
|
[TableName.PamAccount]: KnexOriginal.CompositeTableType<TPamAccounts, TPamAccountsInsert, TPamAccountsUpdate>;
|
||||||
[TableName.PamSession]: KnexOriginal.CompositeTableType<TPamSessions, TPamSessionsInsert, TPamSessionsUpdate>;
|
[TableName.PamSession]: KnexOriginal.CompositeTableType<TPamSessions, TPamSessionsInsert, TPamSessionsUpdate>;
|
||||||
|
|
||||||
|
[TableName.Namespace]: KnexOriginal.CompositeTableType<TNamespaces, TNamespacesInsert, TNamespacesUpdate>;
|
||||||
|
[TableName.Membership]: KnexOriginal.CompositeTableType<TMemberships, TMembershipsInsert, TMembershipsUpdate>;
|
||||||
|
[TableName.MembershipRole]: KnexOriginal.CompositeTableType<
|
||||||
|
TMembershipRoles,
|
||||||
|
TMembershipRolesInsert,
|
||||||
|
TMembershipRolesUpdate
|
||||||
|
>;
|
||||||
|
[TableName.Role]: KnexOriginal.CompositeTableType<TRoles, TRolesInsert, TRolesUpdate>;
|
||||||
|
[TableName.AdditionalPrivilege]: KnexOriginal.CompositeTableType<
|
||||||
|
TAdditionalPrivileges,
|
||||||
|
TAdditionalPrivilegesInsert,
|
||||||
|
TAdditionalPrivilegesUpdate
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,27 @@
|
|||||||
import knex, { Knex } from "knex";
|
import knex, { Knex } from "knex";
|
||||||
|
|
||||||
|
const parseSslConfig = (dbConnectionUri: string, dbRootCert?: string) => {
|
||||||
|
let modifiedDbConnectionUri = dbConnectionUri;
|
||||||
|
let sslConfig: { rejectUnauthorized: boolean; ca: string } | boolean = false;
|
||||||
|
|
||||||
|
if (dbRootCert) {
|
||||||
|
const url = new URL(dbConnectionUri);
|
||||||
|
const sslMode = url.searchParams.get("sslmode");
|
||||||
|
|
||||||
|
if (sslMode && sslMode !== "disable") {
|
||||||
|
url.searchParams.delete("sslmode");
|
||||||
|
modifiedDbConnectionUri = url.toString();
|
||||||
|
|
||||||
|
sslConfig = {
|
||||||
|
rejectUnauthorized: ["verify-ca", "verify-full"].includes(sslMode),
|
||||||
|
ca: Buffer.from(dbRootCert, "base64").toString("ascii")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { modifiedDbConnectionUri, sslConfig };
|
||||||
|
};
|
||||||
|
|
||||||
export type TDbClient = Knex;
|
export type TDbClient = Knex;
|
||||||
export const initDbConnection = ({
|
export const initDbConnection = ({
|
||||||
dbConnectionUri,
|
dbConnectionUri,
|
||||||
@@ -32,23 +54,18 @@ export const initDbConnection = ({
|
|||||||
return selectedReplica;
|
return selectedReplica;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { modifiedDbConnectionUri, sslConfig } = parseSslConfig(dbConnectionUri, dbRootCert);
|
||||||
|
|
||||||
db = knex({
|
db = knex({
|
||||||
client: "pg",
|
client: "pg",
|
||||||
connection: {
|
connection: {
|
||||||
connectionString: dbConnectionUri,
|
connectionString: modifiedDbConnectionUri,
|
||||||
host: process.env.DB_HOST,
|
host: process.env.DB_HOST,
|
||||||
// @ts-expect-error I have no clue why only for the port there is a type error
|
port: process.env.DB_PORT ? parseInt(process.env.DB_PORT, 10) : undefined,
|
||||||
// eslint-disable-next-line
|
|
||||||
port: process.env.DB_PORT,
|
|
||||||
user: process.env.DB_USER,
|
user: process.env.DB_USER,
|
||||||
database: process.env.DB_NAME,
|
database: process.env.DB_NAME,
|
||||||
password: process.env.DB_PASSWORD,
|
password: process.env.DB_PASSWORD,
|
||||||
ssl: dbRootCert
|
ssl: sslConfig
|
||||||
? {
|
|
||||||
rejectUnauthorized: true,
|
|
||||||
ca: Buffer.from(dbRootCert, "base64").toString("ascii")
|
|
||||||
}
|
|
||||||
: false
|
|
||||||
},
|
},
|
||||||
// https://knexjs.org/guide/#pool
|
// https://knexjs.org/guide/#pool
|
||||||
pool: { min: 0, max: 10 },
|
pool: { min: 0, max: 10 },
|
||||||
@@ -59,16 +76,16 @@ export const initDbConnection = ({
|
|||||||
|
|
||||||
readReplicaDbs = readReplicas.map((el) => {
|
readReplicaDbs = readReplicas.map((el) => {
|
||||||
const replicaDbCertificate = el.dbRootCert || dbRootCert;
|
const replicaDbCertificate = el.dbRootCert || dbRootCert;
|
||||||
|
const { modifiedDbConnectionUri: replicaUri, sslConfig: replicaSslConfig } = parseSslConfig(
|
||||||
|
el.dbConnectionUri,
|
||||||
|
replicaDbCertificate
|
||||||
|
);
|
||||||
|
|
||||||
return knex({
|
return knex({
|
||||||
client: "pg",
|
client: "pg",
|
||||||
connection: {
|
connection: {
|
||||||
connectionString: el.dbConnectionUri,
|
connectionString: replicaUri,
|
||||||
ssl: replicaDbCertificate
|
ssl: replicaSslConfig
|
||||||
? {
|
|
||||||
rejectUnauthorized: true,
|
|
||||||
ca: Buffer.from(replicaDbCertificate, "base64").toString("ascii")
|
|
||||||
}
|
|
||||||
: false
|
|
||||||
},
|
},
|
||||||
migrations: {
|
migrations: {
|
||||||
tableName: "infisical_migrations"
|
tableName: "infisical_migrations"
|
||||||
@@ -87,26 +104,21 @@ export const initAuditLogDbConnection = ({
|
|||||||
dbConnectionUri: string;
|
dbConnectionUri: string;
|
||||||
dbRootCert?: string;
|
dbRootCert?: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { modifiedDbConnectionUri, sslConfig } = parseSslConfig(dbConnectionUri, dbRootCert);
|
||||||
|
|
||||||
// akhilmhdh: the default Knex is knex.Knex<any, any[]>. but when assigned with knex({<config>}) the value is knex.Knex<any, unknown[]>
|
// akhilmhdh: the default Knex is knex.Knex<any, any[]>. but when assigned with knex({<config>}) the value is knex.Knex<any, unknown[]>
|
||||||
// this was causing issue with files like `snapshot-dal` `findRecursivelySnapshots` this i am explicitly putting the any and unknown[]
|
// this was causing issue with files like `snapshot-dal` `findRecursivelySnapshots` this i am explicitly putting the any and unknown[]
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const db: Knex<any, unknown[]> = knex({
|
const db: Knex<any, unknown[]> = knex({
|
||||||
client: "pg",
|
client: "pg",
|
||||||
connection: {
|
connection: {
|
||||||
connectionString: dbConnectionUri,
|
connectionString: modifiedDbConnectionUri,
|
||||||
host: process.env.AUDIT_LOGS_DB_HOST,
|
host: process.env.AUDIT_LOGS_DB_HOST,
|
||||||
// @ts-expect-error I have no clue why only for the port there is a type error
|
port: process.env.AUDIT_LOGS_DB_PORT ? parseInt(process.env.AUDIT_LOGS_DB_PORT, 10) : undefined,
|
||||||
// eslint-disable-next-line
|
|
||||||
port: process.env.AUDIT_LOGS_DB_PORT,
|
|
||||||
user: process.env.AUDIT_LOGS_DB_USER,
|
user: process.env.AUDIT_LOGS_DB_USER,
|
||||||
database: process.env.AUDIT_LOGS_DB_NAME,
|
database: process.env.AUDIT_LOGS_DB_NAME,
|
||||||
password: process.env.AUDIT_LOGS_DB_PASSWORD,
|
password: process.env.AUDIT_LOGS_DB_PASSWORD,
|
||||||
ssl: dbRootCert
|
ssl: sslConfig
|
||||||
? {
|
|
||||||
rejectUnauthorized: true,
|
|
||||||
ca: Buffer.from(dbRootCert, "base64").toString("ascii")
|
|
||||||
}
|
|
||||||
: false
|
|
||||||
},
|
},
|
||||||
migrations: {
|
migrations: {
|
||||||
tableName: "infisical_migrations"
|
tableName: "infisical_migrations"
|
||||||
|
|||||||
@@ -127,7 +127,8 @@ export async function down(knex: Knex): Promise<void> {
|
|||||||
});
|
});
|
||||||
await knex.schema.alterTable(TableName.SecretApprovalPolicyApprover, (tb) => {
|
await knex.schema.alterTable(TableName.SecretApprovalPolicyApprover, (tb) => {
|
||||||
tb.dropColumn("approverUserId");
|
tb.dropColumn("approverUserId");
|
||||||
tb.uuid("approverId").notNullable().alter();
|
// akhilmhdh: i had to comment this out and is not possible as membership is now changed in structure
|
||||||
|
// tb.uuid("approverId").notNullable().alter();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1121
backend/src/db/migrations/20251005152640_simplify-membership.ts
Normal file
1121
backend/src/db/migrations/20251005152640_simplify-membership.ts
Normal file
File diff suppressed because it is too large
Load Diff
19
backend/src/db/migrations/20251008003912_relay-heartbeat.ts
Normal file
19
backend/src/db/migrations/20251008003912_relay-heartbeat.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.Relay, "heartbeat"))) {
|
||||||
|
await knex.schema.alterTable(TableName.Relay, (t) => {
|
||||||
|
t.datetime("heartbeat");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.Relay, "heartbeat")) {
|
||||||
|
await knex.schema.alterTable(TableName.Relay, (t) => {
|
||||||
|
t.dropColumn("heartbeat");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.Relay, "healthAlertedAt"))) {
|
||||||
|
await knex.schema.alterTable(TableName.Relay, (t) => {
|
||||||
|
t.datetime("healthAlertedAt");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.GatewayV2, "healthAlertedAt"))) {
|
||||||
|
await knex.schema.alterTable(TableName.GatewayV2, (t) => {
|
||||||
|
t.datetime("healthAlertedAt");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.GatewayV2, "healthAlertedAt")) {
|
||||||
|
await knex.schema.alterTable(TableName.GatewayV2, (t) => {
|
||||||
|
t.dropColumn("healthAlertedAt");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (await knex.schema.hasColumn(TableName.Relay, "healthAlertedAt")) {
|
||||||
|
await knex.schema.alterTable(TableName.Relay, (t) => {
|
||||||
|
t.dropColumn("healthAlertedAt");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.KmsKey, "kmipMetadata"))) {
|
||||||
|
await knex.schema.alterTable(TableName.KmsKey, (t) => {
|
||||||
|
t.jsonb("kmipMetadata");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.KmsKey, "kmipMetadata")) {
|
||||||
|
await knex.schema.alterTable(TableName.KmsKey, (t) => {
|
||||||
|
t.dropColumn("kmipMetadata");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
30
backend/src/db/schemas/additional-privileges.ts
Normal file
30
backend/src/db/schemas/additional-privileges.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// 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 AdditionalPrivilegesSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
name: z.string(),
|
||||||
|
isTemporary: z.boolean().default(false),
|
||||||
|
temporaryMode: z.string().nullable().optional(),
|
||||||
|
temporaryRange: z.string().nullable().optional(),
|
||||||
|
temporaryAccessStartTime: z.date().nullable().optional(),
|
||||||
|
temporaryAccessEndTime: z.date().nullable().optional(),
|
||||||
|
permissions: z.unknown(),
|
||||||
|
actorUserId: z.string().uuid().nullable().optional(),
|
||||||
|
actorIdentityId: z.string().uuid().nullable().optional(),
|
||||||
|
orgId: z.string().uuid().nullable().optional(),
|
||||||
|
projectId: z.string().nullable().optional(),
|
||||||
|
namespaceId: z.string().uuid().nullable().optional(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TAdditionalPrivileges = z.infer<typeof AdditionalPrivilegesSchema>;
|
||||||
|
export type TAdditionalPrivilegesInsert = Omit<z.input<typeof AdditionalPrivilegesSchema>, TImmutableDBKeys>;
|
||||||
|
export type TAdditionalPrivilegesUpdate = Partial<Omit<z.input<typeof AdditionalPrivilegesSchema>, TImmutableDBKeys>>;
|
||||||
@@ -18,7 +18,8 @@ export const GatewaysV2Schema = z.object({
|
|||||||
relayId: z.string().uuid().nullable().optional(),
|
relayId: z.string().uuid().nullable().optional(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
heartbeat: z.date().nullable().optional(),
|
heartbeat: z.date().nullable().optional(),
|
||||||
encryptedPamSessionKey: zodBuffer.nullable().optional()
|
encryptedPamSessionKey: zodBuffer.nullable().optional(),
|
||||||
|
healthAlertedAt: z.date().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TGatewaysV2 = z.infer<typeof GatewaysV2Schema>;
|
export type TGatewaysV2 = z.infer<typeof GatewaysV2Schema>;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export * from "./access-approval-policies-approvers";
|
|||||||
export * from "./access-approval-policies-bypassers";
|
export * from "./access-approval-policies-bypassers";
|
||||||
export * from "./access-approval-requests";
|
export * from "./access-approval-requests";
|
||||||
export * from "./access-approval-requests-reviewers";
|
export * from "./access-approval-requests-reviewers";
|
||||||
|
export * from "./additional-privileges";
|
||||||
export * from "./api-keys";
|
export * from "./api-keys";
|
||||||
export * from "./app-connections";
|
export * from "./app-connections";
|
||||||
export * from "./audit-log-streams";
|
export * from "./audit-log-streams";
|
||||||
@@ -73,8 +74,11 @@ export * from "./kms-keys";
|
|||||||
export * from "./kms-root-config";
|
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 "./membership-roles";
|
||||||
|
export * from "./memberships";
|
||||||
export * from "./microsoft-teams-integrations";
|
export * from "./microsoft-teams-integrations";
|
||||||
export * from "./models";
|
export * from "./models";
|
||||||
|
export * from "./namespaces";
|
||||||
export * from "./oidc-configs";
|
export * from "./oidc-configs";
|
||||||
export * from "./org-bots";
|
export * from "./org-bots";
|
||||||
export * from "./org-gateway-config";
|
export * from "./org-gateway-config";
|
||||||
@@ -108,6 +112,7 @@ export * from "./projects";
|
|||||||
export * from "./rate-limit";
|
export * from "./rate-limit";
|
||||||
export * from "./relays";
|
export * from "./relays";
|
||||||
export * from "./resource-metadata";
|
export * from "./resource-metadata";
|
||||||
|
export * from "./roles";
|
||||||
export * from "./saml-configs";
|
export * from "./saml-configs";
|
||||||
export * from "./scim-tokens";
|
export * from "./scim-tokens";
|
||||||
export * from "./secret-approval-policies";
|
export * from "./secret-approval-policies";
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ export const KmsKeysSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
projectId: z.string().nullable().optional(),
|
projectId: z.string().nullable().optional(),
|
||||||
keyUsage: z.string().default("encrypt-decrypt")
|
keyUsage: z.string().default("encrypt-decrypt"),
|
||||||
|
kmipMetadata: z.unknown().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TKmsKeys = z.infer<typeof KmsKeysSchema>;
|
export type TKmsKeys = z.infer<typeof KmsKeysSchema>;
|
||||||
|
|||||||
26
backend/src/db/schemas/membership-roles.ts
Normal file
26
backend/src/db/schemas/membership-roles.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// 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 MembershipRolesSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
role: z.string(),
|
||||||
|
isTemporary: z.boolean().default(false),
|
||||||
|
temporaryMode: z.string().nullable().optional(),
|
||||||
|
temporaryRange: z.string().nullable().optional(),
|
||||||
|
temporaryAccessStartTime: z.date().nullable().optional(),
|
||||||
|
temporaryAccessEndTime: z.date().nullable().optional(),
|
||||||
|
customRoleId: z.string().uuid().nullable().optional(),
|
||||||
|
membershipId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TMembershipRoles = z.infer<typeof MembershipRolesSchema>;
|
||||||
|
export type TMembershipRolesInsert = Omit<z.input<typeof MembershipRolesSchema>, TImmutableDBKeys>;
|
||||||
|
export type TMembershipRolesUpdate = Partial<Omit<z.input<typeof MembershipRolesSchema>, TImmutableDBKeys>>;
|
||||||
32
backend/src/db/schemas/memberships.ts
Normal file
32
backend/src/db/schemas/memberships.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// Code generated by automation script, DO NOT EDIT.
|
||||||
|
// Automated by pulling database and generating zod schema
|
||||||
|
// To update. Just run npm run generate:schema
|
||||||
|
// Written by akhilmhdh.
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const MembershipsSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
scope: z.string(),
|
||||||
|
actorUserId: z.string().uuid().nullable().optional(),
|
||||||
|
actorIdentityId: z.string().uuid().nullable().optional(),
|
||||||
|
actorGroupId: z.string().uuid().nullable().optional(),
|
||||||
|
scopeOrgId: z.string().uuid(),
|
||||||
|
scopeProjectId: z.string().nullable().optional(),
|
||||||
|
scopeNamespaceId: z.string().uuid().nullable().optional(),
|
||||||
|
isActive: z.boolean().default(true),
|
||||||
|
status: z.string().nullable().optional(),
|
||||||
|
inviteEmail: z.string().nullable().optional(),
|
||||||
|
lastInvitedAt: z.date().nullable().optional(),
|
||||||
|
lastLoginAuthMethod: z.string().nullable().optional(),
|
||||||
|
lastLoginTime: z.date().nullable().optional(),
|
||||||
|
projectFavorites: z.string().array().nullable().optional(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TMemberships = z.infer<typeof MembershipsSchema>;
|
||||||
|
export type TMembershipsInsert = Omit<z.input<typeof MembershipsSchema>, TImmutableDBKeys>;
|
||||||
|
export type TMembershipsUpdate = Partial<Omit<z.input<typeof MembershipsSchema>, TImmutableDBKeys>>;
|
||||||
@@ -178,6 +178,14 @@ export enum TableName {
|
|||||||
SecretScanningScan = "secret_scanning_scans",
|
SecretScanningScan = "secret_scanning_scans",
|
||||||
SecretScanningFinding = "secret_scanning_findings",
|
SecretScanningFinding = "secret_scanning_findings",
|
||||||
SecretScanningConfig = "secret_scanning_configs",
|
SecretScanningConfig = "secret_scanning_configs",
|
||||||
|
|
||||||
|
Membership = "memberships",
|
||||||
|
MembershipRole = "membership_roles",
|
||||||
|
Role = "roles",
|
||||||
|
AdditionalPrivilege = "additional_privileges",
|
||||||
|
|
||||||
|
Namespace = "namespaces",
|
||||||
|
|
||||||
// reminders
|
// reminders
|
||||||
Reminder = "reminders",
|
Reminder = "reminders",
|
||||||
ReminderRecipient = "reminders_recipients",
|
ReminderRecipient = "reminders_recipients",
|
||||||
@@ -302,7 +310,39 @@ export enum ActionProjectType {
|
|||||||
Any = "any"
|
Any = "any"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum TemporaryPermissionMode {
|
||||||
|
Relative = "relative"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MembershipActors {
|
||||||
|
Group = "group",
|
||||||
|
User = "user",
|
||||||
|
Identity = "identity"
|
||||||
|
}
|
||||||
|
|
||||||
export enum SortDirection {
|
export enum SortDirection {
|
||||||
ASC = "asc",
|
ASC = "asc",
|
||||||
DESC = "desc"
|
DESC = "desc"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum AccessScope {
|
||||||
|
Organization = "organization",
|
||||||
|
Namespace = "namespace",
|
||||||
|
Project = "project"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AccessScopeData =
|
||||||
|
| {
|
||||||
|
scope: AccessScope.Organization;
|
||||||
|
orgId: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
scope: AccessScope.Namespace;
|
||||||
|
orgId: string;
|
||||||
|
namespaceId: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
scope: AccessScope.Project;
|
||||||
|
orgId: string;
|
||||||
|
projectId: string;
|
||||||
|
};
|
||||||
|
|||||||
21
backend/src/db/schemas/namespaces.ts
Normal file
21
backend/src/db/schemas/namespaces.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// 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 NamespacesSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
name: z.string(),
|
||||||
|
description: z.string().nullable().optional(),
|
||||||
|
orgId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TNamespaces = z.infer<typeof NamespacesSchema>;
|
||||||
|
export type TNamespacesInsert = Omit<z.input<typeof NamespacesSchema>, TImmutableDBKeys>;
|
||||||
|
export type TNamespacesUpdate = Partial<Omit<z.input<typeof NamespacesSchema>, TImmutableDBKeys>>;
|
||||||
@@ -14,7 +14,9 @@ export const RelaysSchema = z.object({
|
|||||||
orgId: z.string().uuid().nullable().optional(),
|
orgId: z.string().uuid().nullable().optional(),
|
||||||
identityId: z.string().uuid().nullable().optional(),
|
identityId: z.string().uuid().nullable().optional(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
host: z.string()
|
host: z.string(),
|
||||||
|
heartbeat: z.date().nullable().optional(),
|
||||||
|
healthAlertedAt: z.date().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TRelays = z.infer<typeof RelaysSchema>;
|
export type TRelays = z.infer<typeof RelaysSchema>;
|
||||||
|
|||||||
25
backend/src/db/schemas/roles.ts
Normal file
25
backend/src/db/schemas/roles.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// 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 RolesSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
name: z.string(),
|
||||||
|
description: z.string().nullable().optional(),
|
||||||
|
slug: z.string(),
|
||||||
|
permissions: z.unknown(),
|
||||||
|
orgId: z.string().uuid().nullable().optional(),
|
||||||
|
projectId: z.string().nullable().optional(),
|
||||||
|
namespaceId: z.string().uuid().nullable().optional(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TRoles = z.infer<typeof RolesSchema>;
|
||||||
|
export type TRolesInsert = Omit<z.input<typeof RolesSchema>, TImmutableDBKeys>;
|
||||||
|
export type TRolesUpdate = Partial<Omit<z.input<typeof RolesSchema>, TImmutableDBKeys>>;
|
||||||
27
backend/src/db/schemas/user-notifications-default.ts
Normal file
27
backend/src/db/schemas/user-notifications-default.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// 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 UserNotificationsDefaultSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
userId: z.string().uuid(),
|
||||||
|
orgId: z.string().uuid().nullable().optional(),
|
||||||
|
type: z.string(),
|
||||||
|
title: z.string(),
|
||||||
|
body: z.string().nullable().optional(),
|
||||||
|
link: z.string().nullable().optional(),
|
||||||
|
isRead: z.boolean().default(false),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TUserNotificationsDefault = z.infer<typeof UserNotificationsDefaultSchema>;
|
||||||
|
export type TUserNotificationsDefaultInsert = Omit<z.input<typeof UserNotificationsDefaultSchema>, TImmutableDBKeys>;
|
||||||
|
export type TUserNotificationsDefaultUpdate = Partial<
|
||||||
|
Omit<z.input<typeof UserNotificationsDefaultSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { OrgMembershipRole, OrgMembershipStatus, TableName } from "../schemas";
|
import { AccessScope, OrgMembershipRole, OrgMembershipStatus, TableName } from "../schemas";
|
||||||
import { seedData1 } from "../seed-data";
|
import { seedData1 } from "../seed-data";
|
||||||
|
|
||||||
export async function seed(knex: Knex): Promise<void> {
|
export async function seed(knex: Knex): Promise<void> {
|
||||||
@@ -24,13 +24,22 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
])
|
])
|
||||||
.returning("*");
|
.returning("*");
|
||||||
|
|
||||||
await knex(TableName.OrgMembership).insert([
|
const [membership] = await knex(TableName.Membership)
|
||||||
|
.insert([
|
||||||
|
{
|
||||||
|
scope: AccessScope.Organization,
|
||||||
|
scopeOrgId: org.id,
|
||||||
|
actorUserId: user.id,
|
||||||
|
isActive: true,
|
||||||
|
status: OrgMembershipStatus.Accepted
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.returning("*");
|
||||||
|
|
||||||
|
await knex(TableName.MembershipRole).insert([
|
||||||
{
|
{
|
||||||
role: OrgMembershipRole.Admin,
|
membershipId: membership.id,
|
||||||
orgId: org.id,
|
role: OrgMembershipRole.Admin
|
||||||
status: OrgMembershipStatus.Accepted,
|
|
||||||
userId: user.id,
|
|
||||||
isActive: true
|
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,15 @@ import { generateUserSrpKeys } from "@app/lib/crypto/srp";
|
|||||||
import { initLogger, logger } from "@app/lib/logger";
|
import { initLogger, logger } from "@app/lib/logger";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { AuthMethod } from "@app/services/auth/auth-type";
|
import { AuthMethod } from "@app/services/auth/auth-type";
|
||||||
|
import { membershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
||||||
|
import { membershipUserDALFactory } from "@app/services/membership-user/membership-user-dal";
|
||||||
import { assignWorkspaceKeysToMembers, createProjectKey } from "@app/services/project/project-fns";
|
import { assignWorkspaceKeysToMembers, createProjectKey } from "@app/services/project/project-fns";
|
||||||
import { projectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
import { projectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||||
import { projectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
|
||||||
import { projectUserMembershipRoleDALFactory } from "@app/services/project-membership/project-user-membership-role-dal";
|
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
import { userDALFactory } from "@app/services/user/user-dal";
|
import { userDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
AccessScope,
|
||||||
OrgMembershipRole,
|
OrgMembershipRole,
|
||||||
OrgMembershipStatus,
|
OrgMembershipStatus,
|
||||||
ProjectMembershipRole,
|
ProjectMembershipRole,
|
||||||
@@ -39,8 +40,8 @@ const createUserWithGhostUser = async (
|
|||||||
) => {
|
) => {
|
||||||
const projectKeyDAL = projectKeyDALFactory(knex);
|
const projectKeyDAL = projectKeyDALFactory(knex);
|
||||||
const userDAL = userDALFactory(knex);
|
const userDAL = userDALFactory(knex);
|
||||||
const projectMembershipDAL = projectMembershipDALFactory(knex);
|
const membershipDAL = membershipUserDALFactory(knex);
|
||||||
const projectUserMembershipRoleDAL = projectUserMembershipRoleDALFactory(knex);
|
const membershipRoleDAL = membershipRoleDALFactory(knex);
|
||||||
|
|
||||||
const email = `sudo-${alphaNumericNanoId(16)}-${orgId}@infisical.com`; // We add a nanoid because the email is unique. And we have to create a new ghost user each time, so we can have access to the private key.
|
const email = `sudo-${alphaNumericNanoId(16)}-${orgId}@infisical.com`; // We add a nanoid because the email is unique. And we have to create a new ghost user each time, so we can have access to the private key.
|
||||||
|
|
||||||
@@ -63,25 +64,36 @@ const createUserWithGhostUser = async (
|
|||||||
.onConflict("userId")
|
.onConflict("userId")
|
||||||
.merge();
|
.merge();
|
||||||
|
|
||||||
await knex(TableName.OrgMembership)
|
const [orgMembership] = await knex(TableName.Membership)
|
||||||
.insert({
|
.insert({
|
||||||
orgId,
|
scope: AccessScope.Organization,
|
||||||
userId: ghostUser.id,
|
scopeOrgId: orgId,
|
||||||
role: OrgMembershipRole.Admin,
|
actorUserId: ghostUser.id,
|
||||||
status: OrgMembershipStatus.Accepted,
|
status: OrgMembershipStatus.Accepted,
|
||||||
isActive: true
|
isActive: true
|
||||||
})
|
})
|
||||||
.returning("*");
|
.returning("*");
|
||||||
|
|
||||||
const [projectMembership] = await knex(TableName.ProjectMembership)
|
await knex(TableName.MembershipRole).insert([
|
||||||
|
{
|
||||||
|
membershipId: orgMembership.id,
|
||||||
|
role: OrgMembershipRole.Admin
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [projectMembership] = await knex(TableName.Membership)
|
||||||
.insert({
|
.insert({
|
||||||
userId: ghostUser.id,
|
actorUserId: ghostUser.id,
|
||||||
projectId
|
scopeProjectId: projectId,
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
scopeOrgId: orgId,
|
||||||
|
status: OrgMembershipStatus.Accepted,
|
||||||
|
isActive: true
|
||||||
})
|
})
|
||||||
.returning("*");
|
.returning("*");
|
||||||
|
|
||||||
await knex(TableName.ProjectUserMembershipRole).insert({
|
await knex(TableName.MembershipRole).insert({
|
||||||
projectMembershipId: projectMembership.id,
|
membershipId: projectMembership.id,
|
||||||
role: ProjectMembershipRole.Admin
|
role: ProjectMembershipRole.Admin
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -142,17 +154,16 @@ const createUserWithGhostUser = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Create a membership for the user
|
// Create a membership for the user
|
||||||
const userProjectMembership = await projectMembershipDAL.create(
|
const userProjectMembership = await membershipDAL.create(
|
||||||
{
|
{
|
||||||
projectId,
|
scopeProjectId: projectId,
|
||||||
userId: user.id
|
scope: AccessScope.Project,
|
||||||
|
actorUserId: user.id,
|
||||||
|
scopeOrgId: orgId
|
||||||
},
|
},
|
||||||
knex
|
knex
|
||||||
);
|
);
|
||||||
await projectUserMembershipRoleDAL.create(
|
await membershipRoleDAL.create({ membershipId: userProjectMembership.id, role: ProjectMembershipRole.Admin }, knex);
|
||||||
{ projectMembershipId: userProjectMembership.id, role: ProjectMembershipRole.Admin },
|
|
||||||
knex
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create a project key for the user
|
// Create a project key for the user
|
||||||
await projectKeyDAL.create(
|
await projectKeyDAL.create(
|
||||||
@@ -195,10 +206,11 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
})
|
})
|
||||||
.returning("*");
|
.returning("*");
|
||||||
|
|
||||||
const userOrgMembership = await knex(TableName.OrgMembership)
|
const userOrgMembership = await knex(TableName.Membership)
|
||||||
.where({
|
.where({
|
||||||
orgId: seedData1.organization.id,
|
scopeOrgId: seedData1.organization.id,
|
||||||
userId: seedData1.id
|
actorUserId: seedData1.id,
|
||||||
|
scope: AccessScope.Organization
|
||||||
})
|
})
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { ProjectMembershipRole, ProjectType, ProjectVersion, TableName } from "../schemas";
|
import { AccessScope, ProjectMembershipRole, ProjectType, ProjectVersion, TableName } from "../schemas";
|
||||||
import { seedData1 } from "../seed-data";
|
import { seedData1 } from "../seed-data";
|
||||||
|
|
||||||
export const DEFAULT_PROJECT_ENVS = [
|
export const DEFAULT_PROJECT_ENVS = [
|
||||||
@@ -23,15 +23,17 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
})
|
})
|
||||||
.returning("*");
|
.returning("*");
|
||||||
|
|
||||||
const projectMembershipV3 = await knex(TableName.ProjectMembership)
|
const projectMembershipV3 = await knex(TableName.Membership)
|
||||||
.insert({
|
.insert({
|
||||||
projectId: projectV2.id,
|
scopeProjectId: projectV2.id,
|
||||||
userId: seedData1.id
|
actorUserId: seedData1.id,
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
scopeOrgId: seedData1.organization.id
|
||||||
})
|
})
|
||||||
.returning("*");
|
.returning("*");
|
||||||
await knex(TableName.ProjectUserMembershipRole).insert({
|
await knex(TableName.MembershipRole).insert({
|
||||||
role: ProjectMembershipRole.Admin,
|
role: ProjectMembershipRole.Admin,
|
||||||
projectMembershipId: projectMembershipV3[0].id
|
membershipId: projectMembershipV3[0].id
|
||||||
});
|
});
|
||||||
|
|
||||||
// create default environments and default folders
|
// create default environments and default folders
|
||||||
|
|||||||
@@ -5,13 +5,12 @@ import { crypto } from "@app/lib/crypto/cryptography";
|
|||||||
import { initLogger, logger } from "@app/lib/logger";
|
import { initLogger, logger } from "@app/lib/logger";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
|
|
||||||
import { IdentityAuthMethod, OrgMembershipRole, ProjectMembershipRole, TableName } from "../schemas";
|
import { AccessScope, IdentityAuthMethod, OrgMembershipRole, ProjectMembershipRole, TableName } from "../schemas";
|
||||||
import { seedData1 } from "../seed-data";
|
import { seedData1 } from "../seed-data";
|
||||||
|
|
||||||
export async function seed(knex: Knex): Promise<void> {
|
export async function seed(knex: Knex): Promise<void> {
|
||||||
// Deletes ALL existing entries
|
// Deletes ALL existing entries
|
||||||
await knex(TableName.Identity).del();
|
await knex(TableName.Identity).del();
|
||||||
await knex(TableName.IdentityOrgMembership).del();
|
|
||||||
|
|
||||||
initLogger();
|
initLogger();
|
||||||
|
|
||||||
@@ -78,34 +77,47 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
isClientSecretRevoked: false
|
isClientSecretRevoked: false
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
await knex(TableName.IdentityOrgMembership).insert([
|
const [orgMembership] = await knex(TableName.Membership)
|
||||||
|
.insert([
|
||||||
|
{
|
||||||
|
actorIdentityId: seedData1.machineIdentity.id,
|
||||||
|
scopeOrgId: seedData1.organization.id,
|
||||||
|
scope: AccessScope.Organization
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.returning("*");
|
||||||
|
await knex(TableName.MembershipRole).insert([
|
||||||
{
|
{
|
||||||
identityId: seedData1.machineIdentity.id,
|
membershipId: orgMembership.id,
|
||||||
orgId: seedData1.organization.id,
|
|
||||||
role: OrgMembershipRole.Admin
|
role: OrgMembershipRole.Admin
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const identityProjectMembership = await knex(TableName.IdentityProjectMembership)
|
const identityProjectMembership = await knex(TableName.Membership)
|
||||||
.insert({
|
.insert({
|
||||||
identityId: seedData1.machineIdentity.id,
|
actorIdentityId: seedData1.machineIdentity.id,
|
||||||
projectId: seedData1.project.id
|
scopeOrgId: seedData1.organization.id,
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
scopeProjectId: seedData1.project.id
|
||||||
})
|
})
|
||||||
.returning("*");
|
.returning("*");
|
||||||
|
|
||||||
await knex(TableName.IdentityProjectMembershipRole).insert({
|
await knex(TableName.MembershipRole).insert({
|
||||||
role: ProjectMembershipRole.Admin,
|
role: ProjectMembershipRole.Admin,
|
||||||
projectMembershipId: identityProjectMembership[0].id
|
membershipId: identityProjectMembership[0].id
|
||||||
});
|
});
|
||||||
const identityProjectMembershipV3 = await knex(TableName.IdentityProjectMembership)
|
|
||||||
|
const identityProjectMembershipV3 = await knex(TableName.Membership)
|
||||||
.insert({
|
.insert({
|
||||||
identityId: seedData1.machineIdentity.id,
|
actorIdentityId: seedData1.machineIdentity.id,
|
||||||
projectId: seedData1.projectV3.id
|
scopeOrgId: seedData1.organization.id,
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
scopeProjectId: seedData1.projectV3.id
|
||||||
})
|
})
|
||||||
.returning("*");
|
.returning("*");
|
||||||
|
|
||||||
await knex(TableName.IdentityProjectMembershipRole).insert({
|
await knex(TableName.MembershipRole).insert({
|
||||||
role: ProjectMembershipRole.Admin,
|
role: ProjectMembershipRole.Admin,
|
||||||
projectMembershipId: identityProjectMembershipV3[0].id
|
membershipId: identityProjectMembershipV3[0].id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { packRules } from "@casl/ability/extra";
|
import { packRules } from "@casl/ability/extra";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas";
|
import { AccessScope, ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import {
|
import {
|
||||||
backfillPermissionV1SchemaToV2Schema,
|
backfillPermissionV1SchemaToV2Schema,
|
||||||
@@ -13,7 +13,6 @@ import { slugSchema } from "@app/server/lib/schemas";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { SanitizedRoleSchemaV1 } from "@app/server/routes/sanitizedSchemas";
|
import { SanitizedRoleSchemaV1 } from "@app/server/routes/sanitizedSchemas";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { ProjectRoleServiceIdentifierType } from "@app/services/project-role/project-role-types";
|
|
||||||
|
|
||||||
export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProvider) => {
|
export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
@@ -55,14 +54,16 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions, true))
|
packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions, true))
|
||||||
);
|
);
|
||||||
|
|
||||||
const role = await server.services.projectRole.createRole({
|
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
slug: req.params.projectSlug,
|
||||||
actorId: req.permission.id,
|
orgId: req.permission.orgId
|
||||||
actorOrgId: req.permission.orgId,
|
});
|
||||||
actor: req.permission.type,
|
const role = await server.services.role.createRole({
|
||||||
filter: {
|
permission: req.permission,
|
||||||
type: ProjectRoleServiceIdentifierType.SLUG,
|
scopeData: {
|
||||||
projectSlug: req.params.projectSlug
|
scope: AccessScope.Project,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
projectId
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
...req.body,
|
...req.body,
|
||||||
@@ -73,7 +74,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
projectId: role.projectId,
|
projectId,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.CREATE_PROJECT_ROLE,
|
type: EventType.CREATE_PROJECT_ROLE,
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -86,7 +87,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { role };
|
return { role: { ...role, projectId: role.projectId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -131,12 +132,21 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
? JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions, true)))
|
? JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions, true)))
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const role = await server.services.projectRole.updateRole({
|
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
slug: req.params.projectSlug,
|
||||||
actorId: req.permission.id,
|
orgId: req.permission.orgId
|
||||||
actorOrgId: req.permission.orgId,
|
});
|
||||||
actor: req.permission.type,
|
|
||||||
roleId: req.params.roleId,
|
const role = await server.services.role.updateRole({
|
||||||
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
projectId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
id: req.params.roleId
|
||||||
|
},
|
||||||
data: {
|
data: {
|
||||||
...req.body,
|
...req.body,
|
||||||
permissions: stringifiedPermissions
|
permissions: stringifiedPermissions
|
||||||
@@ -146,7 +156,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
projectId: role.projectId,
|
projectId: role.projectId as string,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.UPDATE_PROJECT_ROLE,
|
type: EventType.UPDATE_PROJECT_ROLE,
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -159,7 +169,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { role };
|
return { role: { ...role, projectId: role.projectId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -188,18 +198,27 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.projectRole.deleteRole({
|
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
slug: req.params.projectSlug,
|
||||||
actorId: req.permission.id,
|
orgId: req.permission.orgId
|
||||||
actorOrgId: req.permission.orgId,
|
});
|
||||||
actor: req.permission.type,
|
|
||||||
roleId: req.params.roleId
|
const role = await server.services.role.deleteRole({
|
||||||
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
projectId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
id: req.params.roleId
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
projectId: role.projectId,
|
projectId: role.projectId as string,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.DELETE_PROJECT_ROLE,
|
type: EventType.DELETE_PROJECT_ROLE,
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -210,7 +229,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { role };
|
return { role: { ...role, projectId: role.projectId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -238,17 +257,21 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const roles = await server.services.projectRole.listRoles({
|
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
slug: req.params.projectSlug,
|
||||||
actorId: req.permission.id,
|
orgId: req.permission.orgId
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actor: req.permission.type,
|
|
||||||
filter: {
|
|
||||||
type: ProjectRoleServiceIdentifierType.SLUG,
|
|
||||||
projectSlug: req.params.projectSlug
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return { roles };
|
|
||||||
|
const { roles } = await server.services.role.listRoles({
|
||||||
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
projectId
|
||||||
|
},
|
||||||
|
data: {}
|
||||||
|
});
|
||||||
|
return { roles: roles.map((el) => ({ ...el, projectId: el.projectId as string })) };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -265,78 +288,30 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
role: SanitizedRoleSchemaV1.omit({ version: true })
|
role: SanitizedRoleSchemaV1
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.projectRole.getRoleBySlug({
|
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
slug: req.params.projectSlug,
|
||||||
actorId: req.permission.id,
|
orgId: req.permission.orgId
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actor: req.permission.type,
|
|
||||||
filter: {
|
|
||||||
type: ProjectRoleServiceIdentifierType.SLUG,
|
|
||||||
projectSlug: req.params.projectSlug
|
|
||||||
},
|
|
||||||
roleSlug: req.params.slug
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return { role };
|
const role = await server.services.role.getRoleBySlug({
|
||||||
}
|
permission: req.permission,
|
||||||
});
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
server.route({
|
orgId: req.permission.orgId,
|
||||||
method: "GET",
|
projectId
|
||||||
url: "/:projectId/permissions",
|
},
|
||||||
config: {
|
selector: {
|
||||||
rateLimit: readLimit
|
slug: req.params.slug
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
params: z.object({
|
|
||||||
projectId: z.string().trim()
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
data: z.object({
|
|
||||||
membership: z.object({
|
|
||||||
id: z.string(),
|
|
||||||
roles: z
|
|
||||||
.object({
|
|
||||||
role: z.string()
|
|
||||||
})
|
|
||||||
.array()
|
|
||||||
}),
|
|
||||||
assumedPrivilegeDetails: z
|
|
||||||
.object({
|
|
||||||
actorId: z.string(),
|
|
||||||
actorType: z.string(),
|
|
||||||
actorName: z.string(),
|
|
||||||
actorEmail: z.string().optional()
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
permissions: z.any().array()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
|
||||||
handler: async (req) => {
|
|
||||||
const { permissions, membership, assumedPrivilegeDetails } = await server.services.projectRole.getUserPermission(
|
|
||||||
req.permission.id,
|
|
||||||
req.params.projectId,
|
|
||||||
req.permission.authMethod,
|
|
||||||
req.permission.orgId
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: {
|
|
||||||
permissions,
|
|
||||||
membership,
|
|
||||||
assumedPrivilegeDetails
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
|
return { role: { ...role, projectId: role.projectId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
import { AccessScope, TemporaryPermissionMode } from "@app/db/schemas";
|
||||||
import { backfillPermissionV1SchemaToV2Schema } from "@app/ee/services/permission/project-permission";
|
import { backfillPermissionV1SchemaToV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
import { ApiDocsTags, IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
import { ApiDocsTags, IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||||
import { UnauthorizedError } from "@app/lib/errors";
|
import { UnauthorizedError } from "@app/lib/errors";
|
||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
ProjectSpecificPrivilegePermissionSchema,
|
ProjectSpecificPrivilegePermissionSchema,
|
||||||
SanitizedIdentityPrivilegeSchema
|
SanitizedIdentityPrivilegeSchema
|
||||||
} from "@app/server/routes/sanitizedSchemas";
|
} from "@app/server/routes/sanitizedSchemas";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
@@ -56,6 +56,10 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
if (!permissions && !privilegePermission) {
|
if (!permissions && !privilegePermission) {
|
||||||
throw new UnauthorizedError({ message: "Permission or privilegePermission must be provided" });
|
throw new UnauthorizedError({ message: "Permission or privilegePermission must be provided" });
|
||||||
}
|
}
|
||||||
|
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
slug: req.body.projectSlug
|
||||||
|
});
|
||||||
|
|
||||||
const permission = privilegePermission
|
const permission = privilegePermission
|
||||||
? privilegePermission.actions.map((action) => ({
|
? privilegePermission.actions.map((action) => ({
|
||||||
@@ -64,19 +68,35 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
conditions: privilegePermission.conditions
|
conditions: privilegePermission.conditions
|
||||||
}))
|
}))
|
||||||
: permissions!;
|
: permissions!;
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
|
|
||||||
actorId: req.permission.id,
|
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.createAdditionalPrivilege({
|
||||||
actor: req.permission.type,
|
permission: req.permission,
|
||||||
actorOrgId: req.permission.orgId,
|
scopeData: {
|
||||||
actorAuthMethod: req.permission.authMethod,
|
scope: AccessScope.Project,
|
||||||
...req.body,
|
projectId,
|
||||||
slug: req.body.slug ?? slugify(alphaNumericNanoId(12)),
|
orgId: req.permission.orgId
|
||||||
isTemporary: false,
|
},
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
data: {
|
||||||
// @ts-ignore-error this is valid ts
|
actorId: req.body.identityId,
|
||||||
permissions: backfillPermissionV1SchemaToV2Schema(permission)
|
actorType: ActorType.IDENTITY,
|
||||||
|
...req.body,
|
||||||
|
isTemporary: false,
|
||||||
|
name: req.body.slug || slugify(alphaNumericNanoId(8)),
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore-error this is valid ts
|
||||||
|
permissions: backfillPermissionV1SchemaToV2Schema(permission)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return { privilege };
|
|
||||||
|
return {
|
||||||
|
privilege: {
|
||||||
|
...privilege,
|
||||||
|
identityId: req.body.identityId,
|
||||||
|
projectMembershipId: projectId,
|
||||||
|
projectId,
|
||||||
|
slug: privilege.name
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -106,7 +126,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.privilegePermission
|
IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.privilegePermission
|
||||||
).optional(),
|
).optional(),
|
||||||
temporaryMode: z
|
temporaryMode: z
|
||||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
.nativeEnum(TemporaryPermissionMode)
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
||||||
temporaryRange: z
|
temporaryRange: z
|
||||||
.string()
|
.string()
|
||||||
@@ -138,19 +158,39 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
}))
|
}))
|
||||||
: permissions!;
|
: permissions!;
|
||||||
|
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
|
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||||
actorId: req.permission.id,
|
orgId: req.permission.orgId,
|
||||||
actor: req.permission.type,
|
slug: req.body.projectSlug
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
...req.body,
|
|
||||||
slug: req.body.slug ?? slugify(alphaNumericNanoId(12)),
|
|
||||||
isTemporary: true,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore-error this is valid ts
|
|
||||||
permissions: backfillPermissionV1SchemaToV2Schema(permission)
|
|
||||||
});
|
});
|
||||||
return { privilege };
|
|
||||||
|
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.createAdditionalPrivilege({
|
||||||
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
projectId,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
actorId: req.body.identityId,
|
||||||
|
actorType: ActorType.IDENTITY,
|
||||||
|
...req.body,
|
||||||
|
isTemporary: true,
|
||||||
|
name: req.body.slug || slugify(alphaNumericNanoId(8)),
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore-error this is valid ts
|
||||||
|
permissions: backfillPermissionV1SchemaToV2Schema(permission)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
privilege: {
|
||||||
|
...privilege,
|
||||||
|
identityId: req.body.identityId,
|
||||||
|
projectMembershipId: projectId,
|
||||||
|
projectId,
|
||||||
|
slug: privilege.name
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -183,7 +223,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
).optional(),
|
).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(TemporaryPermissionMode)
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
|
||||||
temporaryRange: z
|
temporaryRange: z
|
||||||
.string()
|
.string()
|
||||||
@@ -216,18 +256,36 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
conditions: privilegePermission.conditions
|
conditions: privilegePermission.conditions
|
||||||
}))
|
}))
|
||||||
: permissions!;
|
: permissions!;
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilege.updateBySlug({
|
|
||||||
actorId: req.permission.id,
|
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||||
actor: req.permission.type,
|
orgId: req.permission.orgId,
|
||||||
actorOrgId: req.permission.orgId,
|
slug: req.body.projectSlug
|
||||||
actorAuthMethod: req.permission.authMethod,
|
});
|
||||||
slug: req.body.privilegeSlug,
|
|
||||||
identityId: req.body.identityId,
|
const { privilege: privilegeDoc } = await server.services.convertor.additionalPrivilegeNameToDoc(
|
||||||
projectSlug: req.body.projectSlug,
|
req.body.privilegeSlug,
|
||||||
|
projectId
|
||||||
|
);
|
||||||
|
|
||||||
|
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.updateAdditionalPrivilege({
|
||||||
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
projectId,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
actorId: req.body.identityId,
|
||||||
|
actorType: ActorType.IDENTITY,
|
||||||
|
id: privilegeDoc.id
|
||||||
|
},
|
||||||
data: {
|
data: {
|
||||||
|
...req.body,
|
||||||
...updatedInfo,
|
...updatedInfo,
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore-error this is valid ts
|
// @ts-ignore-error this is valid ts
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore-error this is valid ts
|
||||||
permissions: permission
|
permissions: permission
|
||||||
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore-error this is valid ts
|
// @ts-ignore-error this is valid ts
|
||||||
@@ -235,7 +293,16 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return { privilege };
|
|
||||||
|
return {
|
||||||
|
privilege: {
|
||||||
|
...privilege,
|
||||||
|
identityId: req.body.identityId,
|
||||||
|
projectMembershipId: projectId,
|
||||||
|
projectId,
|
||||||
|
slug: privilege.name
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -267,16 +334,39 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilege.deleteBySlug({
|
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||||
actorId: req.permission.id,
|
orgId: req.permission.orgId,
|
||||||
actor: req.permission.type,
|
slug: req.body.projectSlug
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
slug: req.body.privilegeSlug,
|
|
||||||
identityId: req.body.identityId,
|
|
||||||
projectSlug: req.body.projectSlug
|
|
||||||
});
|
});
|
||||||
return { privilege };
|
|
||||||
|
const { privilegeId } = await server.services.convertor.additionalPrivilegeNameToDoc(
|
||||||
|
req.body.privilegeSlug,
|
||||||
|
projectId
|
||||||
|
);
|
||||||
|
|
||||||
|
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.deleteAdditionalPrivilege({
|
||||||
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
projectId,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
actorId: req.body.identityId,
|
||||||
|
actorType: ActorType.IDENTITY,
|
||||||
|
id: privilegeId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
privilege: {
|
||||||
|
...privilege,
|
||||||
|
identityId: req.body.identityId,
|
||||||
|
projectMembershipId: projectId,
|
||||||
|
projectId,
|
||||||
|
slug: privilege.name
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -310,15 +400,39 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilege.getPrivilegeDetailsBySlug({
|
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||||
actorId: req.permission.id,
|
orgId: req.permission.orgId,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
slug: req.query.projectSlug
|
||||||
actor: req.permission.type,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
slug: req.params.privilegeSlug,
|
|
||||||
...req.query
|
|
||||||
});
|
});
|
||||||
return { privilege };
|
|
||||||
|
const { privilegeId } = await server.services.convertor.additionalPrivilegeNameToDoc(
|
||||||
|
req.params.privilegeSlug,
|
||||||
|
projectId
|
||||||
|
);
|
||||||
|
|
||||||
|
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.getAdditionalPrivilegeById({
|
||||||
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
projectId,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
actorId: req.query.identityId,
|
||||||
|
actorType: ActorType.IDENTITY,
|
||||||
|
id: privilegeId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
privilege: {
|
||||||
|
...privilege,
|
||||||
|
identityId: req.query.identityId,
|
||||||
|
projectMembershipId: projectId,
|
||||||
|
projectId,
|
||||||
|
slug: privilege.name
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -349,15 +463,32 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const privileges = await server.services.identityProjectAdditionalPrivilege.listIdentityProjectPrivileges({
|
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||||
actorId: req.permission.id,
|
orgId: req.permission.orgId,
|
||||||
actor: req.permission.type,
|
slug: req.query.projectSlug
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
...req.query
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { additionalPrivileges: privileges } = await server.services.additionalPrivilege.listAdditionalPrivileges({
|
||||||
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
projectId,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
actorId: req.query.identityId,
|
||||||
|
actorType: ActorType.IDENTITY
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
privileges
|
privileges: privileges.map((privilege) => ({
|
||||||
|
...privilege,
|
||||||
|
identityId: req.query.identityId,
|
||||||
|
projectMembershipId: projectId,
|
||||||
|
projectId,
|
||||||
|
slug: privilege.name
|
||||||
|
}))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -128,7 +128,8 @@ export const registerKmipSpecRouter = async (server: FastifyZodProvider) => {
|
|||||||
200: z.object({
|
200: z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
value: z.string(),
|
value: z.string(),
|
||||||
algorithm: z.string()
|
algorithm: z.string(),
|
||||||
|
kmipMetadata: z.record(z.any()).optional()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -433,7 +434,8 @@ export const registerKmipSpecRouter = async (server: FastifyZodProvider) => {
|
|||||||
body: z.object({
|
body: z.object({
|
||||||
key: z.string(),
|
key: z.string(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
algorithm: z.nativeEnum(SymmetricKeyAlgorithm)
|
algorithm: z.nativeEnum(SymmetricKeyAlgorithm),
|
||||||
|
kmipMetadata: z.record(z.any()).optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
import { packRules } from "@casl/ability/extra";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { OrgMembershipRole, OrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
|
import { AccessScope, OrgMembershipRole, OrgRolesSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
|
import { OrgPermissionSchema } from "@app/ee/services/permission/org-permission";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { slugSchema } from "@app/server/lib/schemas";
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
@@ -25,8 +27,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
),
|
),
|
||||||
name: z.string().trim(),
|
name: z.string().trim(),
|
||||||
description: z.string().trim().nullish(),
|
description: z.string().trim().nullish(),
|
||||||
// TODO(scott): once UI refactored permissions: OrgPermissionSchema.array()
|
permissions: OrgPermissionSchema.array()
|
||||||
permissions: z.any().array()
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -36,13 +37,18 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.orgRole.createRole(
|
const stringifiedPermissions = JSON.stringify(packRules(req.body.permissions));
|
||||||
req.permission.id,
|
const role = await server.services.role.createRole({
|
||||||
req.params.organizationId,
|
permission: req.permission,
|
||||||
req.body,
|
scopeData: {
|
||||||
req.permission.authMethod,
|
scope: AccessScope.Organization,
|
||||||
req.permission.orgId
|
orgId: req.params.organizationId
|
||||||
);
|
},
|
||||||
|
data: {
|
||||||
|
...req.body,
|
||||||
|
permissions: stringifiedPermissions
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
@@ -59,7 +65,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { role };
|
return { role: { ...role, orgId: role.orgId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -82,14 +88,17 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.orgRole.getRole(
|
const role = await server.services.role.getRoleById({
|
||||||
req.permission.id,
|
permission: req.permission,
|
||||||
req.params.organizationId,
|
scopeData: {
|
||||||
req.params.roleId,
|
scope: AccessScope.Organization,
|
||||||
req.permission.authMethod,
|
orgId: req.params.organizationId
|
||||||
req.permission.orgId
|
},
|
||||||
);
|
selector: {
|
||||||
return { role };
|
id: req.params.roleId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { role: { ...role, orgId: role.orgId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -114,8 +123,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
.optional(),
|
.optional(),
|
||||||
name: z.string().trim().optional(),
|
name: z.string().trim().optional(),
|
||||||
description: z.string().trim().nullish(),
|
description: z.string().trim().nullish(),
|
||||||
// TODO(scott): once UI refactored permissions: OrgPermissionSchema.array().optional()
|
permissions: OrgPermissionSchema.array().optional()
|
||||||
permissions: z.any().array().optional()
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -125,14 +133,21 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.orgRole.updateRole(
|
const stringifiedPermissions = req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined;
|
||||||
req.permission.id,
|
const role = await server.services.role.updateRole({
|
||||||
req.params.organizationId,
|
permission: req.permission,
|
||||||
req.params.roleId,
|
scopeData: {
|
||||||
req.body,
|
scope: AccessScope.Organization,
|
||||||
req.permission.authMethod,
|
orgId: req.params.organizationId
|
||||||
req.permission.orgId
|
},
|
||||||
);
|
selector: {
|
||||||
|
id: req.params.roleId
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
...req.body,
|
||||||
|
permissions: stringifiedPermissions
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
@@ -149,7 +164,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { role };
|
return { role: { ...role, orgId: role.orgId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -172,13 +187,16 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.orgRole.deleteRole(
|
const role = await server.services.role.deleteRole({
|
||||||
req.permission.id,
|
permission: req.permission,
|
||||||
req.params.organizationId,
|
scopeData: {
|
||||||
req.params.roleId,
|
scope: AccessScope.Organization,
|
||||||
req.permission.authMethod,
|
orgId: req.params.organizationId
|
||||||
req.permission.orgId
|
},
|
||||||
);
|
selector: {
|
||||||
|
id: req.params.roleId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
@@ -189,7 +207,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { role };
|
return { role: { ...role, orgId: role.orgId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -206,22 +224,26 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
data: z.object({
|
data: z.object({
|
||||||
roles: OrgRolesSchema.omit({ permissions: true })
|
roles: OrgRolesSchema.omit({ permissions: true }).array()
|
||||||
.merge(z.object({ permissions: z.unknown() }))
|
|
||||||
.array()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const roles = await server.services.orgRole.listRoles(
|
const { roles } = await server.services.role.listRoles({
|
||||||
req.permission.id,
|
permission: req.permission,
|
||||||
req.params.organizationId,
|
scopeData: {
|
||||||
req.permission.authMethod,
|
scope: AccessScope.Organization,
|
||||||
req.permission.orgId
|
orgId: req.permission.orgId
|
||||||
);
|
},
|
||||||
return { data: { roles } };
|
data: {}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
roles: roles.map((el) => ({ ...el, orgId: el.orgId as string }))
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -237,20 +259,33 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
membership: OrgMembershipsSchema,
|
memberships: z
|
||||||
|
.object({
|
||||||
|
id: z.string(),
|
||||||
|
roles: z
|
||||||
|
.object({
|
||||||
|
role: z.string()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
})
|
||||||
|
.array(),
|
||||||
permissions: z.any().array()
|
permissions: z.any().array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { permissions, membership } = await server.services.orgRole.getUserPermission(
|
const { permissions, memberships } = await server.services.role.getUserPermission({
|
||||||
req.permission.id,
|
permission: req.permission,
|
||||||
req.params.organizationId,
|
scopeData: {
|
||||||
req.permission.authMethod,
|
scope: AccessScope.Organization,
|
||||||
req.permission.orgId
|
orgId: req.permission.orgId
|
||||||
);
|
}
|
||||||
return { permissions, membership };
|
});
|
||||||
|
return {
|
||||||
|
permissions,
|
||||||
|
memberships
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { packRules } from "@casl/ability/extra";
|
import { packRules } from "@casl/ability/extra";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas";
|
import { AccessScope, ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
||||||
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
@@ -11,7 +11,6 @@ import { slugSchema } from "@app/server/lib/schemas";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas";
|
import { SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { ProjectRoleServiceIdentifierType } from "@app/services/project-role/project-role-types";
|
|
||||||
|
|
||||||
export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
@@ -55,13 +54,11 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const stringifiedPermissions = JSON.stringify(packRules(req.body.permissions));
|
const stringifiedPermissions = JSON.stringify(packRules(req.body.permissions));
|
||||||
|
|
||||||
const role = await server.services.projectRole.createRole({
|
const role = await server.services.role.createRole({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
scopeData: {
|
||||||
actorOrgId: req.permission.orgId,
|
scope: AccessScope.Project,
|
||||||
actor: req.permission.type,
|
orgId: req.permission.orgId,
|
||||||
filter: {
|
|
||||||
type: ProjectRoleServiceIdentifierType.ID,
|
|
||||||
projectId: req.params.projectId
|
projectId: req.params.projectId
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
@@ -73,7 +70,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
projectId: role.projectId,
|
projectId: role.projectId as string,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.CREATE_PROJECT_ROLE,
|
type: EventType.CREATE_PROJECT_ROLE,
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -86,7 +83,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { role };
|
return { role: { ...role, projectId: role.projectId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -133,12 +130,16 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const stringifiedPermissions = req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined;
|
const stringifiedPermissions = req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined;
|
||||||
const role = await server.services.projectRole.updateRole({
|
const role = await server.services.role.updateRole({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
scopeData: {
|
||||||
actorOrgId: req.permission.orgId,
|
scope: AccessScope.Project,
|
||||||
actor: req.permission.type,
|
orgId: req.permission.orgId,
|
||||||
roleId: req.params.roleId,
|
projectId: req.params.projectId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
id: req.params.roleId
|
||||||
|
},
|
||||||
data: {
|
data: {
|
||||||
...req.body,
|
...req.body,
|
||||||
permissions: stringifiedPermissions
|
permissions: stringifiedPermissions
|
||||||
@@ -148,7 +149,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
projectId: role.projectId,
|
projectId: role.projectId as string,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.UPDATE_PROJECT_ROLE,
|
type: EventType.UPDATE_PROJECT_ROLE,
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -161,7 +162,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { role };
|
return { role: { ...role, projectId: role.projectId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -192,18 +193,22 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.projectRole.deleteRole({
|
const role = await server.services.role.deleteRole({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
scopeData: {
|
||||||
actorOrgId: req.permission.orgId,
|
scope: AccessScope.Project,
|
||||||
actor: req.permission.type,
|
orgId: req.permission.orgId,
|
||||||
roleId: req.params.roleId
|
projectId: req.params.projectId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
id: req.params.roleId
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
projectId: role.projectId,
|
projectId: role.projectId as string,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.DELETE_PROJECT_ROLE,
|
type: EventType.DELETE_PROJECT_ROLE,
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -214,7 +219,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { role };
|
return { role: { ...role, projectId: role.projectId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -244,17 +249,16 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const roles = await server.services.projectRole.listRoles({
|
const { roles } = await server.services.role.listRoles({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
scopeData: {
|
||||||
actorOrgId: req.permission.orgId,
|
scope: AccessScope.Project,
|
||||||
actor: req.permission.type,
|
orgId: req.permission.orgId,
|
||||||
filter: {
|
|
||||||
type: ProjectRoleServiceIdentifierType.ID,
|
|
||||||
projectId: req.params.projectId
|
projectId: req.params.projectId
|
||||||
}
|
},
|
||||||
|
data: {}
|
||||||
});
|
});
|
||||||
return { roles };
|
return { roles: roles.map((el) => ({ ...el, projectId: el.projectId as string })) };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -273,24 +277,25 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
role: SanitizedRoleSchema.omit({ version: true })
|
role: SanitizedRoleSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.projectRole.getRoleBySlug({
|
const role = await server.services.role.getRoleBySlug({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
scopeData: {
|
||||||
actorOrgId: req.permission.orgId,
|
scope: AccessScope.Project,
|
||||||
actor: req.permission.type,
|
orgId: req.permission.orgId,
|
||||||
filter: {
|
|
||||||
type: ProjectRoleServiceIdentifierType.ID,
|
|
||||||
projectId: req.params.projectId
|
projectId: req.params.projectId
|
||||||
},
|
},
|
||||||
roleSlug: req.params.roleSlug
|
selector: {
|
||||||
|
slug: req.params.roleSlug
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return { role };
|
|
||||||
|
return { role: { ...role, projectId: role.projectId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -307,14 +312,16 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
data: z.object({
|
data: z.object({
|
||||||
membership: z.object({
|
memberships: z
|
||||||
id: z.string(),
|
.object({
|
||||||
roles: z
|
id: z.string(),
|
||||||
.object({
|
roles: z
|
||||||
role: z.string()
|
.object({
|
||||||
})
|
role: z.string()
|
||||||
.array()
|
})
|
||||||
}),
|
.array()
|
||||||
|
})
|
||||||
|
.array(),
|
||||||
assumedPrivilegeDetails: z
|
assumedPrivilegeDetails: z
|
||||||
.object({
|
.object({
|
||||||
actorId: z.string(),
|
actorId: z.string(),
|
||||||
@@ -330,17 +337,19 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { permissions, membership, assumedPrivilegeDetails } = await server.services.projectRole.getUserPermission(
|
const { permissions, memberships, assumedPrivilegeDetails } = await server.services.role.getUserPermission({
|
||||||
req.permission.id,
|
permission: req.permission,
|
||||||
req.params.projectId,
|
scopeData: {
|
||||||
req.permission.authMethod,
|
scope: AccessScope.Project,
|
||||||
req.permission.orgId
|
projectId: req.params.projectId,
|
||||||
);
|
orgId: req.permission.orgId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: {
|
data: {
|
||||||
permissions,
|
permissions,
|
||||||
membership,
|
memberships,
|
||||||
assumedPrivilegeDetails
|
assumedPrivilegeDetails
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -146,4 +146,85 @@ export const registerRelayRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/heartbeat-instance-relay",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
name: slugSchema({ min: 1, max: 32, field: "name" })
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
message: z.string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: (req, _, next) => {
|
||||||
|
const authHeader = req.headers.authorization;
|
||||||
|
|
||||||
|
if (!appCfg.RELAY_AUTH_SECRET) {
|
||||||
|
throw new UnauthorizedError({
|
||||||
|
message: "Relay authentication not configured"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!authHeader) {
|
||||||
|
throw new UnauthorizedError({
|
||||||
|
message: "Missing authorization header"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedHeader = `Bearer ${appCfg.RELAY_AUTH_SECRET}`;
|
||||||
|
if (
|
||||||
|
authHeader.length === expectedHeader.length &&
|
||||||
|
crypto.nativeCrypto.timingSafeEqual(Buffer.from(authHeader), Buffer.from(expectedHeader))
|
||||||
|
) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnauthorizedError({
|
||||||
|
message: "Invalid relay auth secret"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
await server.services.relay.heartbeat({
|
||||||
|
name: req.body.name
|
||||||
|
});
|
||||||
|
|
||||||
|
return { message: "Successfully triggered heartbeat" };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/heartbeat-org-relay",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
name: slugSchema({ min: 1, max: 32, field: "name" })
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
message: z.string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
await server.services.relay.heartbeat({
|
||||||
|
name: req.body.name,
|
||||||
|
identityId: req.permission.id,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod
|
||||||
|
});
|
||||||
|
|
||||||
|
return { message: "Successfully triggered heartbeat" };
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ const AccessListEntrySchema = z
|
|||||||
.object({
|
.object({
|
||||||
allowedActions: z.nativeEnum(ProjectPermissionSecretActions).array(),
|
allowedActions: z.nativeEnum(ProjectPermissionSecretActions).array(),
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
membershipId: z.string(),
|
|
||||||
name: z.string()
|
name: z.string()
|
||||||
})
|
})
|
||||||
.array();
|
.array();
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { AccessScope, TemporaryPermissionMode } from "@app/db/schemas";
|
||||||
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
||||||
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types";
|
|
||||||
import { PROJECT_USER_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
import { PROJECT_USER_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||||
|
import { NotFoundError } from "@app/lib/errors";
|
||||||
import { ms } from "@app/lib/ms";
|
import { ms } from "@app/lib/ms";
|
||||||
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 { slugSchema } from "@app/server/lib/schemas";
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { SanitizedUserProjectAdditionalPrivilegeSchema } from "@app/server/routes/sanitizedSchema/user-additional-privilege";
|
import { SanitizedUserProjectAdditionalPrivilegeSchema } from "@app/server/routes/sanitizedSchema/user-additional-privilege";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
@@ -34,7 +35,7 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
z.object({
|
z.object({
|
||||||
isTemporary: z.literal(true),
|
isTemporary: z.literal(true),
|
||||||
temporaryMode: z
|
temporaryMode: z
|
||||||
.nativeEnum(ProjectUserAdditionalPrivilegeTemporaryMode)
|
.nativeEnum(TemporaryPermissionMode)
|
||||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
||||||
temporaryRange: z
|
temporaryRange: z
|
||||||
.string()
|
.string()
|
||||||
@@ -55,17 +56,31 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const privilege = await server.services.projectUserAdditionalPrivilege.create({
|
const { userId, membership } = await server.services.convertor.userMembershipIdToUserId(
|
||||||
actorId: req.permission.id,
|
req.body.projectMembershipId,
|
||||||
actor: req.permission.type,
|
AccessScope.Project,
|
||||||
actorOrgId: req.permission.orgId,
|
req.permission.orgId
|
||||||
actorAuthMethod: req.permission.authMethod,
|
);
|
||||||
projectMembershipId: req.body.projectMembershipId,
|
|
||||||
...req.body.type,
|
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.createAdditionalPrivilege({
|
||||||
slug: req.body.slug || slugify(alphaNumericNanoId(8)),
|
permission: req.permission,
|
||||||
permissions: req.body.permissions
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
projectId: membership.scopeProjectId as string,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
actorId: userId,
|
||||||
|
actorType: ActorType.USER,
|
||||||
|
...req.body.type,
|
||||||
|
name: req.body.slug || slugify(alphaNumericNanoId(8)),
|
||||||
|
permissions: req.body.permissions
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return { privilege };
|
|
||||||
|
return {
|
||||||
|
privilege: { ...privilege, userId, projectId: membership.scopeProjectId as string, slug: privilege.name }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -91,7 +106,7 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
z.object({
|
z.object({
|
||||||
isTemporary: z.literal(true).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
|
isTemporary: z.literal(true).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
|
||||||
temporaryMode: z
|
temporaryMode: z
|
||||||
.nativeEnum(ProjectUserAdditionalPrivilegeTemporaryMode)
|
.nativeEnum(TemporaryPermissionMode)
|
||||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
|
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
|
||||||
temporaryRange: z
|
temporaryRange: z
|
||||||
.string()
|
.string()
|
||||||
@@ -113,21 +128,41 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const privilege = await server.services.projectUserAdditionalPrivilege.updateById({
|
const data = await server.services.convertor.additionalPrivilegeIdToDoc(req.params.privilegeId);
|
||||||
actorId: req.permission.id,
|
if (!data.privilege.actorUserId)
|
||||||
actor: req.permission.type,
|
throw new NotFoundError({ message: `Privilege with id ${req.params.privilegeId} not found` });
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.updateAdditionalPrivilege({
|
||||||
...req.body,
|
permission: req.permission,
|
||||||
...req.body.type,
|
scopeData: {
|
||||||
permissions: req.body.permissions
|
scope: AccessScope.Project,
|
||||||
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
projectId: data.privilege.projectId as string,
|
||||||
// @ts-ignore-error this is valid ts
|
orgId: req.permission.orgId
|
||||||
req.body.permissions
|
},
|
||||||
: undefined,
|
data: {
|
||||||
privilegeId: req.params.privilegeId
|
...req.body,
|
||||||
|
...req.body.type,
|
||||||
|
permissions: req.body.permissions
|
||||||
|
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore-error this is valid ts
|
||||||
|
req.body.permissions
|
||||||
|
: undefined
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
id: req.params.privilegeId,
|
||||||
|
actorId: data.privilege.actorUserId,
|
||||||
|
actorType: ActorType.USER
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return { privilege };
|
|
||||||
|
return {
|
||||||
|
privilege: {
|
||||||
|
...privilege,
|
||||||
|
userId: data.privilege.actorUserId,
|
||||||
|
projectId: data.privilege.projectId as string,
|
||||||
|
slug: privilege.name
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -149,14 +184,32 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const privilege = await server.services.projectUserAdditionalPrivilege.deleteById({
|
const data = await server.services.convertor.additionalPrivilegeIdToDoc(req.params.privilegeId);
|
||||||
actorId: req.permission.id,
|
if (!data.privilege.actorUserId)
|
||||||
actor: req.permission.type,
|
throw new NotFoundError({ message: `Privilege with id ${req.params.privilegeId} not found` });
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.deleteAdditionalPrivilege({
|
||||||
privilegeId: req.params.privilegeId
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
projectId: data.privilege.projectId as string,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
id: req.params.privilegeId,
|
||||||
|
actorId: data.privilege.actorUserId,
|
||||||
|
actorType: ActorType.USER
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return { privilege };
|
|
||||||
|
return {
|
||||||
|
privilege: {
|
||||||
|
...privilege,
|
||||||
|
userId: data.privilege.actorUserId,
|
||||||
|
projectId: data.privilege.projectId as string,
|
||||||
|
slug: privilege.name
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -178,14 +231,33 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const privileges = await server.services.projectUserAdditionalPrivilege.listPrivileges({
|
const { userId, membership } = await server.services.convertor.userMembershipIdToUserId(
|
||||||
actorId: req.permission.id,
|
req.query.projectMembershipId,
|
||||||
actor: req.permission.type,
|
AccessScope.Project,
|
||||||
actorOrgId: req.permission.orgId,
|
req.permission.orgId
|
||||||
actorAuthMethod: req.permission.authMethod,
|
);
|
||||||
projectMembershipId: req.query.projectMembershipId
|
|
||||||
|
const { additionalPrivileges: privileges } = await server.services.additionalPrivilege.listAdditionalPrivileges({
|
||||||
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
projectId: membership.scopeProjectId as string,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
actorId: userId,
|
||||||
|
actorType: ActorType.USER
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return { privileges };
|
|
||||||
|
return {
|
||||||
|
privileges: privileges.map((privilege) => ({
|
||||||
|
...privilege,
|
||||||
|
userId: membership.actorUserId as string,
|
||||||
|
projectId: membership.scopeProjectId as string,
|
||||||
|
slug: privilege.name
|
||||||
|
}))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -207,14 +279,32 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const privilege = await server.services.projectUserAdditionalPrivilege.getPrivilegeDetailsById({
|
const data = await server.services.convertor.additionalPrivilegeIdToDoc(req.params.privilegeId);
|
||||||
actorId: req.permission.id,
|
if (!data.privilege.actorUserId)
|
||||||
actor: req.permission.type,
|
throw new NotFoundError({ message: `Privilege with id ${req.params.privilegeId} not found` });
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.getAdditionalPrivilegeById({
|
||||||
privilegeId: req.params.privilegeId
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
projectId: data.privilege.projectId as string,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
id: req.params.privilegeId,
|
||||||
|
actorId: data.privilege.actorUserId,
|
||||||
|
actorType: ActorType.USER
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return { privilege };
|
|
||||||
|
return {
|
||||||
|
privilege: {
|
||||||
|
...privilege,
|
||||||
|
userId: data.privilege.actorUserId,
|
||||||
|
projectId: data.privilege.projectId as string,
|
||||||
|
slug: privilege.name
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { packRules } from "@casl/ability/extra";
|
import { packRules } from "@casl/ability/extra";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas";
|
import { AccessScope, ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
||||||
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
@@ -11,7 +11,6 @@ import { slugSchema } from "@app/server/lib/schemas";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas";
|
import { SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { ProjectRoleServiceIdentifierType } from "@app/services/project-role/project-role-types";
|
|
||||||
|
|
||||||
export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProvider) => {
|
export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
@@ -55,13 +54,11 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const stringifiedPermissions = JSON.stringify(packRules(req.body.permissions));
|
const stringifiedPermissions = JSON.stringify(packRules(req.body.permissions));
|
||||||
|
|
||||||
const role = await server.services.projectRole.createRole({
|
const role = await server.services.role.createRole({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
scopeData: {
|
||||||
actorOrgId: req.permission.orgId,
|
scope: AccessScope.Project,
|
||||||
actor: req.permission.type,
|
orgId: req.permission.orgId,
|
||||||
filter: {
|
|
||||||
type: ProjectRoleServiceIdentifierType.ID,
|
|
||||||
projectId: req.params.projectId
|
projectId: req.params.projectId
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
@@ -73,7 +70,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
projectId: role.projectId,
|
projectId: req.params.projectId,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.CREATE_PROJECT_ROLE,
|
type: EventType.CREATE_PROJECT_ROLE,
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -86,7 +83,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { role };
|
return { role: { ...role, projectId: role.projectId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -133,12 +130,16 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const stringifiedPermissions = req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined;
|
const stringifiedPermissions = req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined;
|
||||||
const role = await server.services.projectRole.updateRole({
|
const role = await server.services.role.updateRole({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
scopeData: {
|
||||||
actorOrgId: req.permission.orgId,
|
scope: AccessScope.Project,
|
||||||
actor: req.permission.type,
|
orgId: req.permission.orgId,
|
||||||
roleId: req.params.roleId,
|
projectId: req.params.projectId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
id: req.params.roleId
|
||||||
|
},
|
||||||
data: {
|
data: {
|
||||||
...req.body,
|
...req.body,
|
||||||
permissions: stringifiedPermissions
|
permissions: stringifiedPermissions
|
||||||
@@ -148,7 +149,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
projectId: role.projectId,
|
projectId: role.projectId as string,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.UPDATE_PROJECT_ROLE,
|
type: EventType.UPDATE_PROJECT_ROLE,
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -161,7 +162,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { role };
|
return { role: { ...role, projectId: role.projectId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -192,18 +193,22 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.projectRole.deleteRole({
|
const role = await server.services.role.deleteRole({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
scopeData: {
|
||||||
actorOrgId: req.permission.orgId,
|
scope: AccessScope.Project,
|
||||||
actor: req.permission.type,
|
orgId: req.permission.orgId,
|
||||||
roleId: req.params.roleId
|
projectId: req.params.projectId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
id: req.params.roleId
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
projectId: role.projectId,
|
projectId: role.projectId as string,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.DELETE_PROJECT_ROLE,
|
type: EventType.DELETE_PROJECT_ROLE,
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -214,7 +219,7 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { role };
|
return { role: { ...role, projectId: role.projectId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -244,17 +249,16 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const roles = await server.services.projectRole.listRoles({
|
const { roles } = await server.services.role.listRoles({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
scopeData: {
|
||||||
actorOrgId: req.permission.orgId,
|
scope: AccessScope.Project,
|
||||||
actor: req.permission.type,
|
orgId: req.permission.orgId,
|
||||||
filter: {
|
|
||||||
type: ProjectRoleServiceIdentifierType.ID,
|
|
||||||
projectId: req.params.projectId
|
projectId: req.params.projectId
|
||||||
}
|
},
|
||||||
|
data: {}
|
||||||
});
|
});
|
||||||
return { roles };
|
return { roles: roles.map((el) => ({ ...el, projectId: el.projectId as string })) };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -273,24 +277,25 @@ export const registerDeprecatedProjectRoleRouter = async (server: FastifyZodProv
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
role: SanitizedRoleSchema.omit({ version: true })
|
role: SanitizedRoleSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const role = await server.services.projectRole.getRoleBySlug({
|
const role = await server.services.role.getRoleBySlug({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
scopeData: {
|
||||||
actorOrgId: req.permission.orgId,
|
scope: AccessScope.Project,
|
||||||
actor: req.permission.type,
|
orgId: req.permission.orgId,
|
||||||
filter: {
|
|
||||||
type: ProjectRoleServiceIdentifierType.ID,
|
|
||||||
projectId: req.params.projectId
|
projectId: req.params.projectId
|
||||||
},
|
},
|
||||||
roleSlug: req.params.roleSlug
|
selector: {
|
||||||
|
slug: req.params.roleSlug
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return { role };
|
|
||||||
|
return { role: { ...role, projectId: role.projectId as string } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-types";
|
import { AccessScope, TemporaryPermissionMode } from "@app/db/schemas";
|
||||||
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
||||||
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
import { ApiDocsTags, IDENTITY_ADDITIONAL_PRIVILEGE_V2 } from "@app/lib/api-docs";
|
import { ApiDocsTags, IDENTITY_ADDITIONAL_PRIVILEGE_V2 } from "@app/lib/api-docs";
|
||||||
@@ -11,7 +11,7 @@ import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
|||||||
import { slugSchema } from "@app/server/lib/schemas";
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { SanitizedIdentityPrivilegeSchema } from "@app/server/routes/sanitizedSchema/identitiy-additional-privilege";
|
import { SanitizedIdentityPrivilegeSchema } from "@app/server/routes/sanitizedSchema/identitiy-additional-privilege";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
@@ -43,7 +43,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
z.object({
|
z.object({
|
||||||
isTemporary: z.literal(true),
|
isTemporary: z.literal(true),
|
||||||
temporaryMode: z
|
temporaryMode: z
|
||||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
.nativeEnum(TemporaryPermissionMode)
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.CREATE.temporaryMode),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.CREATE.temporaryMode),
|
||||||
temporaryRange: z
|
temporaryRange: z
|
||||||
.string()
|
.string()
|
||||||
@@ -64,18 +64,31 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilegeV2.create({
|
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.createAdditionalPrivilege({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
scopeData: {
|
||||||
actorOrgId: req.permission.orgId,
|
scope: AccessScope.Project,
|
||||||
actor: req.permission.type,
|
projectId: req.body.projectId,
|
||||||
projectId: req.body.projectId,
|
orgId: req.permission.orgId
|
||||||
identityId: req.body.identityId,
|
},
|
||||||
...req.body.type,
|
data: {
|
||||||
slug: req.body.slug || slugify(alphaNumericNanoId(8)),
|
actorId: req.body.identityId,
|
||||||
permissions: req.body.permissions
|
actorType: ActorType.IDENTITY,
|
||||||
|
...req.body.type,
|
||||||
|
name: req.body.slug || slugify(alphaNumericNanoId(8)),
|
||||||
|
permissions: req.body.permissions
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return { privilege };
|
|
||||||
|
return {
|
||||||
|
privilege: {
|
||||||
|
...privilege,
|
||||||
|
identityId: req.body.identityId,
|
||||||
|
projectMembershipId: req.body.projectId,
|
||||||
|
projectId: req.body.projectId,
|
||||||
|
slug: privilege.name
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -108,7 +121,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
z.object({
|
z.object({
|
||||||
isTemporary: z.literal(true).describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.UPDATE.isTemporary),
|
isTemporary: z.literal(true).describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.UPDATE.isTemporary),
|
||||||
temporaryMode: z
|
temporaryMode: z
|
||||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
.nativeEnum(TemporaryPermissionMode)
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.UPDATE.temporaryMode),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.UPDATE.temporaryMode),
|
||||||
temporaryRange: z
|
temporaryRange: z
|
||||||
.string()
|
.string()
|
||||||
@@ -129,19 +142,36 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilegeV2.updateById({
|
const { privilege: privilegeDoc } = await server.services.convertor.additionalPrivilegeIdToDoc(req.params.id);
|
||||||
actorId: req.permission.id,
|
|
||||||
actor: req.permission.type,
|
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.updateAdditionalPrivilege({
|
||||||
actorOrgId: req.permission.orgId,
|
permission: req.permission,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
scopeData: {
|
||||||
id: req.params.id,
|
scope: AccessScope.Project,
|
||||||
|
projectId: privilegeDoc.projectId as string,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
id: req.params.id,
|
||||||
|
actorId: privilegeDoc.actorIdentityId as string,
|
||||||
|
actorType: ActorType.IDENTITY
|
||||||
|
},
|
||||||
data: {
|
data: {
|
||||||
...req.body,
|
...req.body,
|
||||||
...req.body.type,
|
...req.body.type,
|
||||||
permissions: req.body.permissions || undefined
|
permissions: req.body.permissions || undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return { privilege };
|
|
||||||
|
return {
|
||||||
|
privilege: {
|
||||||
|
...privilege,
|
||||||
|
identityId: privilegeDoc.actorIdentityId as string,
|
||||||
|
projectMembershipId: privilegeDoc.projectId as string,
|
||||||
|
projectId: privilegeDoc.projectId as string,
|
||||||
|
slug: privilege.name
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -171,14 +201,31 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilegeV2.deleteById({
|
const { privilege: privilegeDoc } = await server.services.convertor.additionalPrivilegeIdToDoc(req.params.id);
|
||||||
actorId: req.permission.id,
|
|
||||||
actor: req.permission.type,
|
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.deleteAdditionalPrivilege({
|
||||||
actorAuthMethod: req.permission.authMethod,
|
permission: req.permission,
|
||||||
actorOrgId: req.permission.orgId,
|
scopeData: {
|
||||||
id: req.params.id
|
scope: AccessScope.Project,
|
||||||
|
projectId: privilegeDoc.projectId as string,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
id: req.params.id,
|
||||||
|
actorId: privilegeDoc.actorIdentityId as string,
|
||||||
|
actorType: ActorType.IDENTITY
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return { privilege };
|
|
||||||
|
return {
|
||||||
|
privilege: {
|
||||||
|
...privilege,
|
||||||
|
identityId: privilegeDoc.actorIdentityId as string,
|
||||||
|
projectMembershipId: privilegeDoc.projectId as string,
|
||||||
|
projectId: privilegeDoc.projectId as string,
|
||||||
|
slug: privilege.name
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -208,14 +255,31 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilegeV2.getPrivilegeDetailsById({
|
const { privilege: privilegeDoc } = await server.services.convertor.additionalPrivilegeIdToDoc(req.params.id);
|
||||||
actorId: req.permission.id,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.getAdditionalPrivilegeById({
|
||||||
actor: req.permission.type,
|
permission: req.permission,
|
||||||
actorOrgId: req.permission.orgId,
|
scopeData: {
|
||||||
id: req.params.id
|
scope: AccessScope.Project,
|
||||||
|
projectId: privilegeDoc.projectId as string,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
id: req.params.id,
|
||||||
|
actorId: privilegeDoc.actorIdentityId as string,
|
||||||
|
actorType: ActorType.IDENTITY
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return { privilege };
|
|
||||||
|
return {
|
||||||
|
privilege: {
|
||||||
|
...privilege,
|
||||||
|
identityId: privilegeDoc.actorIdentityId as string,
|
||||||
|
projectMembershipId: privilegeDoc.projectId as string,
|
||||||
|
projectId: privilegeDoc.projectId as string,
|
||||||
|
slug: privilege.name
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -249,15 +313,36 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const privilege = await server.services.identityProjectAdditionalPrivilegeV2.getPrivilegeDetailsBySlug({
|
const { id: projectId } = await server.services.convertor.projectSlugToId({
|
||||||
actorId: req.permission.id,
|
slug: req.query.projectSlug,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
orgId: req.permission.orgId
|
||||||
actor: req.permission.type,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
slug: req.params.privilegeSlug,
|
|
||||||
...req.query
|
|
||||||
});
|
});
|
||||||
return { privilege };
|
|
||||||
|
const { additionalPrivilege: privilege } = await server.services.additionalPrivilege.getAdditionalPrivilegeByName(
|
||||||
|
{
|
||||||
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
projectId,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
name: req.params.privilegeSlug,
|
||||||
|
actorId: req.query.identityId,
|
||||||
|
actorType: ActorType.IDENTITY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
privilege: {
|
||||||
|
...privilege,
|
||||||
|
identityId: req.query.identityId,
|
||||||
|
projectMembershipId: privilege.projectId as string,
|
||||||
|
projectId,
|
||||||
|
slug: privilege.name
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -288,15 +373,27 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const privileges = await server.services.identityProjectAdditionalPrivilegeV2.listIdentityProjectPrivileges({
|
const { additionalPrivileges: privileges } = await server.services.additionalPrivilege.listAdditionalPrivileges({
|
||||||
actorId: req.permission.id,
|
permission: req.permission,
|
||||||
actor: req.permission.type,
|
scopeData: {
|
||||||
actorAuthMethod: req.permission.authMethod,
|
scope: AccessScope.Project,
|
||||||
actorOrgId: req.permission.orgId,
|
projectId: req.query.projectId,
|
||||||
...req.query
|
orgId: req.permission.orgId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
actorId: req.query.identityId,
|
||||||
|
actorType: ActorType.IDENTITY
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
privileges
|
privileges: privileges.map((privilege) => ({
|
||||||
|
...privilege,
|
||||||
|
identityId: req.query.identityId,
|
||||||
|
projectMembershipId: privilege.projectId as string,
|
||||||
|
projectId: req.query.projectId,
|
||||||
|
slug: privilege.name
|
||||||
|
}))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { AccessScope, ActionProjectType } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { groupBy } from "@app/lib/fn";
|
import { groupBy } from "@app/lib/fn";
|
||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
import { TAdditionalPrivilegeDALFactory } from "@app/services/additional-privilege/additional-privilege-dal";
|
||||||
|
import { TMembershipUserDALFactory } from "@app/services/membership-user/membership-user-dal";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
||||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
import { TAccessApprovalRequestDALFactory } from "../access-approval-request/access-approval-request-dal";
|
import { TAccessApprovalRequestDALFactory } from "../access-approval-request/access-approval-request-dal";
|
||||||
import { TAccessApprovalRequestReviewerDALFactory } from "../access-approval-request/access-approval-request-reviewer-dal";
|
import { TAccessApprovalRequestReviewerDALFactory } from "../access-approval-request/access-approval-request-reviewer-dal";
|
||||||
import { ApprovalStatus } from "../access-approval-request/access-approval-request-types";
|
import { ApprovalStatus } from "../access-approval-request/access-approval-request-types";
|
||||||
import { TGroupDALFactory } from "../group/group-dal";
|
import { TGroupDALFactory } from "../group/group-dal";
|
||||||
import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal";
|
|
||||||
import {
|
import {
|
||||||
TAccessApprovalPolicyApproverDALFactory,
|
TAccessApprovalPolicyApproverDALFactory,
|
||||||
TAccessApprovalPolicyBypasserDALFactory
|
TAccessApprovalPolicyBypasserDALFactory
|
||||||
@@ -39,14 +38,13 @@ type TAccessApprovalPolicyServiceFactoryDep = {
|
|||||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findOne">;
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findOne">;
|
||||||
accessApprovalPolicyApproverDAL: TAccessApprovalPolicyApproverDALFactory;
|
accessApprovalPolicyApproverDAL: TAccessApprovalPolicyApproverDALFactory;
|
||||||
accessApprovalPolicyBypasserDAL: TAccessApprovalPolicyBypasserDALFactory;
|
accessApprovalPolicyBypasserDAL: TAccessApprovalPolicyBypasserDALFactory;
|
||||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
|
|
||||||
groupDAL: TGroupDALFactory;
|
groupDAL: TGroupDALFactory;
|
||||||
userDAL: Pick<TUserDALFactory, "find">;
|
userDAL: Pick<TUserDALFactory, "find">;
|
||||||
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "update" | "find" | "resetReviewByPolicyId">;
|
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "update" | "find" | "resetReviewByPolicyId">;
|
||||||
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
additionalPrivilegeDAL: Pick<TAdditionalPrivilegeDALFactory, "delete">;
|
||||||
accessApprovalRequestReviewerDAL: Pick<TAccessApprovalRequestReviewerDALFactory, "update" | "delete">;
|
accessApprovalRequestReviewerDAL: Pick<TAccessApprovalRequestReviewerDALFactory, "update" | "delete">;
|
||||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "find">;
|
|
||||||
accessApprovalPolicyEnvironmentDAL: TAccessApprovalPolicyEnvironmentDALFactory;
|
accessApprovalPolicyEnvironmentDAL: TAccessApprovalPolicyEnvironmentDALFactory;
|
||||||
|
membershipUserDAL: TMembershipUserDALFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const accessApprovalPolicyServiceFactory = ({
|
export const accessApprovalPolicyServiceFactory = ({
|
||||||
@@ -62,7 +60,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
accessApprovalRequestDAL,
|
accessApprovalRequestDAL,
|
||||||
additionalPrivilegeDAL,
|
additionalPrivilegeDAL,
|
||||||
accessApprovalRequestReviewerDAL,
|
accessApprovalRequestReviewerDAL,
|
||||||
orgMembershipDAL
|
membershipUserDAL
|
||||||
}: TAccessApprovalPolicyServiceFactoryDep): TAccessApprovalPolicyServiceFactory => {
|
}: TAccessApprovalPolicyServiceFactoryDep): TAccessApprovalPolicyServiceFactory => {
|
||||||
const $policyExists = async ({
|
const $policyExists = async ({
|
||||||
envId,
|
envId,
|
||||||
@@ -424,13 +422,14 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
|
|
||||||
// Validate user bypassers
|
// Validate user bypassers
|
||||||
if (bypasserUserIds.length > 0) {
|
if (bypasserUserIds.length > 0) {
|
||||||
const orgMemberships = await orgMembershipDAL.find({
|
const orgMemberships = await membershipUserDAL.find({
|
||||||
$in: { userId: bypasserUserIds },
|
$in: { actorUserId: bypasserUserIds },
|
||||||
orgId: actorOrgId
|
scopeOrgId: actorOrgId,
|
||||||
|
scope: AccessScope.Organization
|
||||||
});
|
});
|
||||||
|
|
||||||
if (orgMemberships.length !== bypasserUserIds.length) {
|
if (orgMemberships.length !== bypasserUserIds.length) {
|
||||||
const foundUserIdsInOrg = new Set(orgMemberships.map((mem) => mem.userId));
|
const foundUserIdsInOrg = new Set(orgMemberships.map((mem) => mem.actorUserId as string));
|
||||||
const missingUserIds = bypasserUserIds.filter((id) => !foundUserIdsInOrg.has(id));
|
const missingUserIds = bypasserUserIds.filter((id) => !foundUserIdsInOrg.has(id));
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: `One or more specified bypasser users are not part of the organization or do not exist. Invalid or non-member user IDs: ${missingUserIds.join(", ")}`
|
message: `One or more specified bypasser users are not part of the organization or do not exist. Invalid or non-member user IDs: ${missingUserIds.join(", ")}`
|
||||||
@@ -633,7 +632,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
|
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
const { membership } = await permissionService.getProjectPermission({
|
await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
@@ -641,9 +640,6 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
actionProjectType: ActionProjectType.SecretManager
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
if (!membership) {
|
|
||||||
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const environment = await projectEnvDAL.findOne({ projectId: project.id, slug: envSlug });
|
const environment = await projectEnvDAL.findOne({ projectId: project.id, slug: envSlug });
|
||||||
if (!environment) throw new NotFoundError({ message: `Environment with slug '${envSlug}' not found` });
|
if (!environment) throw new NotFoundError({ message: `Environment with slug '${envSlug}' not found` });
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ import { Knex } from "knex";
|
|||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import {
|
import {
|
||||||
AccessApprovalRequestsSchema,
|
AccessApprovalRequestsSchema,
|
||||||
|
AccessScope,
|
||||||
TableName,
|
TableName,
|
||||||
TAccessApprovalRequests,
|
TAccessApprovalRequests,
|
||||||
TOrgMemberships,
|
TMemberships,
|
||||||
TUserGroupMembership,
|
TUserGroupMembership,
|
||||||
TUsers
|
TUsers
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
@@ -244,11 +245,10 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
|
|||||||
const docs = await db
|
const docs = await db
|
||||||
.replicaNode()(TableName.AccessApprovalRequest)
|
.replicaNode()(TableName.AccessApprovalRequest)
|
||||||
.whereIn(`${TableName.AccessApprovalRequest}.policyId`, policyIds)
|
.whereIn(`${TableName.AccessApprovalRequest}.policyId`, policyIds)
|
||||||
|
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.ProjectUserAdditionalPrivilege,
|
TableName.AdditionalPrivilege,
|
||||||
`${TableName.AccessApprovalRequest}.privilegeId`,
|
`${TableName.AccessApprovalRequest}.privilegeId`,
|
||||||
`${TableName.ProjectUserAdditionalPrivilege}.id`
|
`${TableName.AdditionalPrivilege}.id`
|
||||||
)
|
)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.AccessApprovalPolicy,
|
TableName.AccessApprovalPolicy,
|
||||||
@@ -276,7 +276,6 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
|
|||||||
`${TableName.UserGroupMembership}.groupId`
|
`${TableName.UserGroupMembership}.groupId`
|
||||||
)
|
)
|
||||||
.leftJoin(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
.leftJoin(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
||||||
|
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.AccessApprovalPolicyBypasser,
|
TableName.AccessApprovalPolicyBypasser,
|
||||||
`${TableName.AccessApprovalPolicy}.id`,
|
`${TableName.AccessApprovalPolicy}.id`,
|
||||||
@@ -294,24 +293,24 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
|
|||||||
`requestedByUser.id`
|
`requestedByUser.id`
|
||||||
)
|
)
|
||||||
|
|
||||||
.leftJoin<TOrgMemberships>(
|
.leftJoin<TMemberships>(db(TableName.Membership).as("approverOrgMembership"), (qb) => {
|
||||||
db(TableName.OrgMembership).as("approverOrgMembership"),
|
qb.on(
|
||||||
`${TableName.AccessApprovalPolicyApprover}.approverUserId`,
|
`${TableName.AccessApprovalPolicyApprover}.approverUserId`,
|
||||||
`approverOrgMembership.userId`
|
`approverOrgMembership.actorUserId`
|
||||||
)
|
).andOn(`approverOrgMembership.scope`, db.raw("?", [AccessScope.Organization]));
|
||||||
|
})
|
||||||
.leftJoin<TOrgMemberships>(
|
.leftJoin<TMemberships>(db(TableName.Membership).as("approverGroupOrgMembership"), (qb) => {
|
||||||
db(TableName.OrgMembership).as("approverGroupOrgMembership"),
|
qb.on(`${TableName.Users}.id`, `approverGroupOrgMembership.actorUserId`).andOn(
|
||||||
`${TableName.Users}.id`,
|
`approverGroupOrgMembership.scope`,
|
||||||
`approverGroupOrgMembership.userId`
|
db.raw("?", [AccessScope.Organization])
|
||||||
)
|
);
|
||||||
|
})
|
||||||
.leftJoin<TOrgMemberships>(
|
.leftJoin<TMemberships>(db(TableName.Membership).as("reviewerOrgMembership"), (qb) => {
|
||||||
db(TableName.OrgMembership).as("reviewerOrgMembership"),
|
qb.on(
|
||||||
`${TableName.AccessApprovalRequestReviewer}.reviewerUserId`,
|
`${TableName.AccessApprovalRequestReviewer}.reviewerUserId`,
|
||||||
`reviewerOrgMembership.userId`
|
`reviewerOrgMembership.actorUserId`
|
||||||
)
|
).andOn(`reviewerOrgMembership.scope`, db.raw("?", [AccessScope.Organization]));
|
||||||
|
})
|
||||||
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
||||||
|
|
||||||
.select(selectAllTableCols(TableName.AccessApprovalRequest))
|
.select(selectAllTableCols(TableName.AccessApprovalRequest))
|
||||||
@@ -360,22 +359,22 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
|
|||||||
db.ref("firstName").withSchema("requestedByUser").as("requestedByUserFirstName"),
|
db.ref("firstName").withSchema("requestedByUser").as("requestedByUserFirstName"),
|
||||||
db.ref("lastName").withSchema("requestedByUser").as("requestedByUserLastName"),
|
db.ref("lastName").withSchema("requestedByUser").as("requestedByUserLastName"),
|
||||||
|
|
||||||
db.ref("userId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeUserId"),
|
db.ref("actorUserId").withSchema(TableName.AdditionalPrivilege).as("privilegeUserId"),
|
||||||
db.ref("projectId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeMembershipId"),
|
db.ref("projectId").withSchema(TableName.AdditionalPrivilege).as("privilegeMembershipId"),
|
||||||
|
|
||||||
db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeIsTemporary"),
|
db.ref("isTemporary").withSchema(TableName.AdditionalPrivilege).as("privilegeIsTemporary"),
|
||||||
db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeTemporaryMode"),
|
db.ref("temporaryMode").withSchema(TableName.AdditionalPrivilege).as("privilegeTemporaryMode"),
|
||||||
db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeTemporaryRange"),
|
db.ref("temporaryRange").withSchema(TableName.AdditionalPrivilege).as("privilegeTemporaryRange"),
|
||||||
db
|
db
|
||||||
.ref("temporaryAccessStartTime")
|
.ref("temporaryAccessStartTime")
|
||||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
.withSchema(TableName.AdditionalPrivilege)
|
||||||
.as("privilegeTemporaryAccessStartTime"),
|
.as("privilegeTemporaryAccessStartTime"),
|
||||||
db
|
db
|
||||||
.ref("temporaryAccessEndTime")
|
.ref("temporaryAccessEndTime")
|
||||||
.withSchema(TableName.ProjectUserAdditionalPrivilege)
|
.withSchema(TableName.AdditionalPrivilege)
|
||||||
.as("privilegeTemporaryAccessEndTime"),
|
.as("privilegeTemporaryAccessEndTime"),
|
||||||
|
|
||||||
db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegePermissions")
|
db.ref("permissions").withSchema(TableName.AdditionalPrivilege).as("privilegePermissions")
|
||||||
)
|
)
|
||||||
.orderBy(`${TableName.AccessApprovalRequest}.createdAt`, "desc");
|
.orderBy(`${TableName.AccessApprovalRequest}.createdAt`, "desc");
|
||||||
|
|
||||||
@@ -408,7 +407,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
|
|||||||
privilege: doc.privilegeId
|
privilege: doc.privilegeId
|
||||||
? {
|
? {
|
||||||
membershipId: doc.privilegeMembershipId,
|
membershipId: doc.privilegeMembershipId,
|
||||||
userId: doc.privilegeUserId,
|
userId: doc.privilegeUserId || "",
|
||||||
projectId: doc.projectId,
|
projectId: doc.projectId,
|
||||||
isTemporary: doc.privilegeIsTemporary,
|
isTemporary: doc.privilegeIsTemporary,
|
||||||
temporaryMode: doc.privilegeTemporaryMode,
|
temporaryMode: doc.privilegeTemporaryMode,
|
||||||
@@ -773,9 +772,9 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
|
|||||||
)
|
)
|
||||||
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.ProjectUserAdditionalPrivilege,
|
TableName.AdditionalPrivilege,
|
||||||
`${TableName.AccessApprovalRequest}.privilegeId`,
|
`${TableName.AccessApprovalRequest}.privilegeId`,
|
||||||
`${TableName.ProjectUserAdditionalPrivilege}.id`
|
`${TableName.AdditionalPrivilege}.id`
|
||||||
)
|
)
|
||||||
|
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import msFn from "ms";
|
import msFn from "ms";
|
||||||
|
|
||||||
import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
|
import { ActionProjectType, ProjectMembershipRole, TemporaryPermissionMode } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { groupBy } from "@app/lib/fn";
|
import { groupBy } from "@app/lib/fn";
|
||||||
@@ -10,12 +10,12 @@ import { alphaNumericNanoId } from "@app/lib/nanoid";
|
|||||||
import { EnforcementLevel } from "@app/lib/types";
|
import { EnforcementLevel } from "@app/lib/types";
|
||||||
import { triggerWorkflowIntegrationNotification } from "@app/lib/workflow-integrations/trigger-notification";
|
import { triggerWorkflowIntegrationNotification } from "@app/lib/workflow-integrations/trigger-notification";
|
||||||
import { TriggerFeature } from "@app/lib/workflow-integrations/types";
|
import { TriggerFeature } from "@app/lib/workflow-integrations/types";
|
||||||
|
import { TAdditionalPrivilegeDALFactory } from "@app/services/additional-privilege/additional-privilege-dal";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { TMicrosoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service";
|
import { TMicrosoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service";
|
||||||
import { TProjectMicrosoftTeamsConfigDALFactory } from "@app/services/microsoft-teams/project-microsoft-teams-config-dal";
|
import { TProjectMicrosoftTeamsConfigDALFactory } from "@app/services/microsoft-teams/project-microsoft-teams-config-dal";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
||||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
|
||||||
import { TProjectSlackConfigDALFactory } from "@app/services/slack/project-slack-config-dal";
|
import { TProjectSlackConfigDALFactory } from "@app/services/slack/project-slack-config-dal";
|
||||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
@@ -26,15 +26,13 @@ import { TAccessApprovalPolicyApproverDALFactory } from "../access-approval-poli
|
|||||||
import { TAccessApprovalPolicyDALFactory } from "../access-approval-policy/access-approval-policy-dal";
|
import { TAccessApprovalPolicyDALFactory } from "../access-approval-policy/access-approval-policy-dal";
|
||||||
import { TGroupDALFactory } from "../group/group-dal";
|
import { TGroupDALFactory } from "../group/group-dal";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service-types";
|
import { TPermissionServiceFactory } from "../permission/permission-service-types";
|
||||||
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 { TAccessApprovalRequestDALFactory } from "./access-approval-request-dal";
|
||||||
import { verifyRequestedPermissions } from "./access-approval-request-fns";
|
import { verifyRequestedPermissions } from "./access-approval-request-fns";
|
||||||
import { TAccessApprovalRequestReviewerDALFactory } from "./access-approval-request-reviewer-dal";
|
import { TAccessApprovalRequestReviewerDALFactory } from "./access-approval-request-reviewer-dal";
|
||||||
import { ApprovalStatus, TAccessApprovalRequestServiceFactory } from "./access-approval-request-types";
|
import { ApprovalStatus, TAccessApprovalRequestServiceFactory } from "./access-approval-request-types";
|
||||||
|
|
||||||
type TSecretApprovalRequestServiceFactoryDep = {
|
type TSecretApprovalRequestServiceFactoryDep = {
|
||||||
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "create" | "findById">;
|
additionalPrivilegeDAL: Pick<TAdditionalPrivilegeDALFactory, "create" | "findById">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "invalidateProjectPermissionCache">;
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "invalidateProjectPermissionCache">;
|
||||||
accessApprovalPolicyApproverDAL: Pick<TAccessApprovalPolicyApproverDALFactory, "find">;
|
accessApprovalPolicyApproverDAL: Pick<TAccessApprovalPolicyApproverDALFactory, "find">;
|
||||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
||||||
@@ -59,7 +57,6 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
|||||||
"create" | "find" | "findOne" | "transaction" | "delete"
|
"create" | "find" | "findOne" | "transaction" | "delete"
|
||||||
>;
|
>;
|
||||||
groupDAL: Pick<TGroupDALFactory, "findAllGroupPossibleMembers">;
|
groupDAL: Pick<TGroupDALFactory, "findAllGroupPossibleMembers">;
|
||||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findById">;
|
|
||||||
smtpService: Pick<TSmtpService, "sendMail">;
|
smtpService: Pick<TSmtpService, "sendMail">;
|
||||||
userDAL: Pick<
|
userDAL: Pick<
|
||||||
TUserDALFactory,
|
TUserDALFactory,
|
||||||
@@ -125,7 +122,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
// Anyone can create an access approval request.
|
// Anyone can create an access approval request.
|
||||||
const { membership } = await permissionService.getProjectPermission({
|
await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
@@ -133,9 +130,6 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
actionProjectType: ActionProjectType.SecretManager
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
if (!membership) {
|
|
||||||
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestedByUser = await userDAL.findById(actorId);
|
const requestedByUser = await userDAL.findById(actorId);
|
||||||
if (!requestedByUser) throw new ForbiddenRequestError({ message: "User not found" });
|
if (!requestedByUser) throw new ForbiddenRequestError({ message: "User not found" });
|
||||||
@@ -340,7 +334,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission({
|
const { hasRole } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId: accessApprovalRequest.projectId,
|
projectId: accessApprovalRequest.projectId,
|
||||||
@@ -349,10 +343,6 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
actionProjectType: ActionProjectType.SecretManager
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!membership) {
|
|
||||||
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const isApprover = policy.approvers.find((approver) => approver.userId === actorId);
|
const isApprover = policy.approvers.find((approver) => approver.userId === actorId);
|
||||||
|
|
||||||
if (!hasRole(ProjectMembershipRole.Admin) && !isApprover) {
|
if (!hasRole(ProjectMembershipRole.Admin) && !isApprover) {
|
||||||
@@ -496,7 +486,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
const { membership } = await permissionService.getProjectPermission({
|
await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
@@ -504,9 +494,6 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
actionProjectType: ActionProjectType.SecretManager
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
if (!membership) {
|
|
||||||
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const policies = await accessApprovalPolicyDAL.find({ projectId: project.id });
|
const policies = await accessApprovalPolicyDAL.find({ projectId: project.id });
|
||||||
let requests = await accessApprovalRequestDAL.findRequestsWithPrivilegeByPolicyIds(policies.map((p) => p.id));
|
let requests = await accessApprovalRequestDAL.findRequestsWithPrivilegeByPolicyIds(policies.map((p) => p.id));
|
||||||
@@ -566,7 +553,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
slug: permissionEnvironment
|
slug: permissionEnvironment
|
||||||
});
|
});
|
||||||
|
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission({
|
const { hasRole } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId: accessApprovalRequest.projectId,
|
projectId: accessApprovalRequest.projectId,
|
||||||
@@ -575,10 +562,6 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
actionProjectType: ActionProjectType.SecretManager
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!membership) {
|
|
||||||
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const isSelfApproval = actorId === accessApprovalRequest.requestedByUserId;
|
const isSelfApproval = actorId === accessApprovalRequest.requestedByUserId;
|
||||||
const isSoftEnforcement = policy.enforcementLevel === EnforcementLevel.Soft;
|
const isSoftEnforcement = policy.enforcementLevel === EnforcementLevel.Soft;
|
||||||
const canBypass = !policy.bypassers.length || policy.bypassers.some((bypasser) => bypasser.userId === actorId);
|
const canBypass = !policy.bypassers.length || policy.bypassers.some((bypasser) => bypasser.userId === actorId);
|
||||||
@@ -724,9 +707,9 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
// Permanent access
|
// Permanent access
|
||||||
const privilege = await additionalPrivilegeDAL.create(
|
const privilege = await additionalPrivilegeDAL.create(
|
||||||
{
|
{
|
||||||
userId: accessApprovalRequest.requestedByUserId,
|
actorUserId: accessApprovalRequest.requestedByUserId,
|
||||||
projectId: accessApprovalRequest.projectId,
|
projectId: accessApprovalRequest.projectId,
|
||||||
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
name: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
||||||
permissions: JSON.stringify(accessApprovalRequest.permissions)
|
permissions: JSON.stringify(accessApprovalRequest.permissions)
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
@@ -739,12 +722,12 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
|
|
||||||
const privilege = await additionalPrivilegeDAL.create(
|
const privilege = await additionalPrivilegeDAL.create(
|
||||||
{
|
{
|
||||||
userId: accessApprovalRequest.requestedByUserId,
|
actorUserId: accessApprovalRequest.requestedByUserId,
|
||||||
projectId: accessApprovalRequest.projectId,
|
projectId: accessApprovalRequest.projectId,
|
||||||
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
name: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
||||||
permissions: JSON.stringify(accessApprovalRequest.permissions),
|
permissions: JSON.stringify(accessApprovalRequest.permissions),
|
||||||
isTemporary: true, // Explicitly set to true for the privilege
|
isTemporary: true, // Explicitly set to true for the privilege
|
||||||
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
|
temporaryMode: TemporaryPermissionMode.Relative,
|
||||||
temporaryRange: accessApprovalRequest.temporaryRange!,
|
temporaryRange: accessApprovalRequest.temporaryRange!,
|
||||||
temporaryAccessStartTime: startTime,
|
temporaryAccessStartTime: startTime,
|
||||||
temporaryAccessEndTime: new Date(startTime.getTime() + relativeTempAllocatedTimeInMs)
|
temporaryAccessEndTime: new Date(startTime.getTime() + relativeTempAllocatedTimeInMs)
|
||||||
@@ -830,7 +813,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
const { membership } = await permissionService.getProjectPermission({
|
await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
@@ -838,9 +821,6 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
actionProjectType: ActionProjectType.SecretManager
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
if (!membership) {
|
|
||||||
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const count = await accessApprovalRequestDAL.getCount({ projectId: project.id, policyId });
|
const count = await accessApprovalRequestDAL.getCount({ projectId: project.id, policyId });
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export const ElasticSearchProvider = (): TDynamicProviderFns => {
|
|||||||
|
|
||||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretElasticSearchSchema>) => {
|
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretElasticSearchSchema>) => {
|
||||||
const connection = new ElasticSearchClient({
|
const connection = new ElasticSearchClient({
|
||||||
|
requestTimeout: 30_000,
|
||||||
node: {
|
node: {
|
||||||
url: new URL(`${providerInputs.host}:${providerInputs.port}`),
|
url: new URL(`${providerInputs.host}:${providerInputs.port}`),
|
||||||
...(providerInputs.ca && {
|
...(providerInputs.ca && {
|
||||||
|
|||||||
@@ -10,20 +10,34 @@ export type TGatewayV2DALFactory = ReturnType<typeof gatewayV2DalFactory>;
|
|||||||
export const gatewayV2DalFactory = (db: TDbClient) => {
|
export const gatewayV2DalFactory = (db: TDbClient) => {
|
||||||
const orm = ormify(db, TableName.GatewayV2);
|
const orm = ormify(db, TableName.GatewayV2);
|
||||||
|
|
||||||
const find = async (filter: TFindFilter<TGatewaysV2>, { offset, limit, sort, tx }: TFindOpt<TGatewaysV2> = {}) => {
|
const find = async (
|
||||||
|
filter: TFindFilter<TGatewaysV2> & { isHeartbeatStale?: boolean },
|
||||||
|
{ offset, limit, sort, tx }: TFindOpt<TGatewaysV2> = {}
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
|
const { isHeartbeatStale, ...regularFilter } = filter;
|
||||||
|
|
||||||
const query = (tx || db.replicaNode())(TableName.GatewayV2)
|
const query = (tx || db.replicaNode())(TableName.GatewayV2)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
.where(buildFindFilter(filter, TableName.GatewayV2))
|
.where(buildFindFilter(regularFilter, TableName.GatewayV2))
|
||||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.GatewayV2}.identityId`)
|
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.GatewayV2}.identityId`)
|
||||||
.join(
|
|
||||||
TableName.IdentityOrgMembership,
|
|
||||||
`${TableName.IdentityOrgMembership}.identityId`,
|
|
||||||
`${TableName.GatewayV2}.identityId`
|
|
||||||
)
|
|
||||||
.select(selectAllTableCols(TableName.GatewayV2))
|
.select(selectAllTableCols(TableName.GatewayV2))
|
||||||
.select(db.ref("name").withSchema(TableName.Identity).as("identityName"));
|
.select(db.ref("name").withSchema(TableName.Identity).as("identityName"));
|
||||||
|
|
||||||
|
if (isHeartbeatStale) {
|
||||||
|
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
|
||||||
|
void query.where(`${TableName.GatewayV2}.heartbeat`, "<", oneHourAgo);
|
||||||
|
void query.where((v) => {
|
||||||
|
void v
|
||||||
|
.whereNull(`${TableName.GatewayV2}.healthAlertedAt`)
|
||||||
|
.orWhere(
|
||||||
|
`${TableName.GatewayV2}.healthAlertedAt`,
|
||||||
|
"<",
|
||||||
|
db.ref("heartbeat").withSchema(TableName.GatewayV2)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (limit) void query.limit(limit);
|
if (limit) void query.limit(limit);
|
||||||
if (offset) void query.offset(offset);
|
if (offset) void query.offset(offset);
|
||||||
if (sort) {
|
if (sort) {
|
||||||
|
|||||||
@@ -2,14 +2,17 @@ import net from "node:net";
|
|||||||
|
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
|
import { CronJob } from "cron";
|
||||||
|
|
||||||
import { TRelays } from "@app/db/schemas";
|
import { OrgMembershipRole, TRelays } from "@app/db/schemas";
|
||||||
import { PgSqlLock } from "@app/keystore/keystore";
|
import { PgSqlLock } from "@app/keystore/keystore";
|
||||||
import { crypto } from "@app/lib/crypto";
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { DatabaseErrorCode } from "@app/lib/error-codes";
|
import { DatabaseErrorCode } from "@app/lib/error-codes";
|
||||||
import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors";
|
||||||
|
import { groupBy } from "@app/lib/fn";
|
||||||
import { GatewayProxyProtocol } from "@app/lib/gateway/types";
|
import { GatewayProxyProtocol } from "@app/lib/gateway/types";
|
||||||
import { withGatewayV2Proxy } from "@app/lib/gateway-v2/gateway-v2";
|
import { withGatewayV2Proxy } from "@app/lib/gateway-v2/gateway-v2";
|
||||||
|
import { logger } from "@app/lib/logger";
|
||||||
import { OrgServiceActor } from "@app/lib/types";
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
||||||
import { constructPemChainFromCerts } from "@app/services/certificate/certificate-fns";
|
import { constructPemChainFromCerts } from "@app/services/certificate/certificate-fns";
|
||||||
@@ -20,6 +23,10 @@ import {
|
|||||||
} from "@app/services/certificate-authority/certificate-authority-fns";
|
} from "@app/services/certificate-authority/certificate-authority-fns";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
|
import { TNotificationServiceFactory } from "@app/services/notification/notification-service";
|
||||||
|
import { NotificationType } from "@app/services/notification/notification-types";
|
||||||
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
|
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
|
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { PamResource } from "../pam-resource/pam-resource-enums";
|
import { PamResource } from "../pam-resource/pam-resource-enums";
|
||||||
@@ -39,6 +46,9 @@ type TGatewayV2ServiceFactoryDep = {
|
|||||||
gatewayV2DAL: TGatewayV2DALFactory;
|
gatewayV2DAL: TGatewayV2DALFactory;
|
||||||
relayDAL: TRelayDALFactory;
|
relayDAL: TRelayDALFactory;
|
||||||
permissionService: TPermissionServiceFactory;
|
permissionService: TPermissionServiceFactory;
|
||||||
|
orgDAL: Pick<TOrgDALFactory, "findOrgMembersByRole">;
|
||||||
|
notificationService: Pick<TNotificationServiceFactory, "createUserNotifications">;
|
||||||
|
smtpService: Pick<TSmtpService, "sendMail">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGatewayV2ServiceFactory = ReturnType<typeof gatewayV2ServiceFactory>;
|
export type TGatewayV2ServiceFactory = ReturnType<typeof gatewayV2ServiceFactory>;
|
||||||
@@ -50,7 +60,10 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
relayService,
|
relayService,
|
||||||
gatewayV2DAL,
|
gatewayV2DAL,
|
||||||
relayDAL,
|
relayDAL,
|
||||||
permissionService
|
permissionService,
|
||||||
|
orgDAL,
|
||||||
|
notificationService,
|
||||||
|
smtpService
|
||||||
}: TGatewayV2ServiceFactoryDep) => {
|
}: TGatewayV2ServiceFactoryDep) => {
|
||||||
const $validateIdentityAccessToGateway = async (orgId: string, actorId: string, actorAuthMethod: ActorAuthMethod) => {
|
const $validateIdentityAccessToGateway = async (orgId: string, actorId: string, actorAuthMethod: ActorAuthMethod) => {
|
||||||
const orgLicensePlan = await licenseService.getPlan(orgId);
|
const orgLicensePlan = await licenseService.getPlan(orgId);
|
||||||
@@ -878,6 +891,72 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const $healthcheckNotify = async () => {
|
||||||
|
const unhealthyGateways = await gatewayV2DAL.find({
|
||||||
|
isHeartbeatStale: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (unhealthyGateways.length === 0) return;
|
||||||
|
|
||||||
|
logger.warn(
|
||||||
|
{ gatewayIds: unhealthyGateways.map((g) => g.id) },
|
||||||
|
"Found gateways with last heartbeat over an hour ago. Sending notifications."
|
||||||
|
);
|
||||||
|
|
||||||
|
const gatewaysByOrg = groupBy(unhealthyGateways, (gw) => gw.orgId);
|
||||||
|
|
||||||
|
for await (const [orgId, gateways] of Object.entries(gatewaysByOrg)) {
|
||||||
|
try {
|
||||||
|
const admins = await orgDAL.findOrgMembersByRole(orgId, OrgMembershipRole.Admin);
|
||||||
|
if (admins.length === 0) {
|
||||||
|
logger.warn({ orgId }, "Organization has no admins to notify about unhealthy gateway.");
|
||||||
|
// eslint-disable-next-line no-continue
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gatewayNames = gateways.map((g) => `"${g.name}"`).join(", ");
|
||||||
|
const body = `The following gateway(s) in your organization may be offline as they haven't reported a heartbeat in over an hour: ${gatewayNames}. Please check their status.`;
|
||||||
|
|
||||||
|
await notificationService.createUserNotifications(
|
||||||
|
admins.map((admin) => ({
|
||||||
|
userId: admin.user.id,
|
||||||
|
orgId,
|
||||||
|
type: NotificationType.GATEWAY_HEALTH_ALERT,
|
||||||
|
title: "Gateway Health Alert",
|
||||||
|
body,
|
||||||
|
link: "/organization/networking"
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
await smtpService.sendMail({
|
||||||
|
recipients: admins.map((admin) => admin.user.email).filter((v): v is string => !!v),
|
||||||
|
subjectLine: "Gateway Health Alert",
|
||||||
|
substitutions: {
|
||||||
|
type: "gateway",
|
||||||
|
names: gatewayNames
|
||||||
|
},
|
||||||
|
template: SmtpTemplates.HealthAlert
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(gateways.map((gw) => gatewayV2DAL.updateById(gw.id, { healthAlertedAt: new Date() })));
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error, `Failed to send gateway health notifications for organization [orgId=${orgId}]`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const initializeHealthcheckNotify = async () => {
|
||||||
|
logger.info("Setting up background notification process for gateway v2 health-checks");
|
||||||
|
|
||||||
|
await $healthcheckNotify();
|
||||||
|
|
||||||
|
// run every 5 minutes
|
||||||
|
const job = new CronJob("*/5 * * * *", $healthcheckNotify);
|
||||||
|
job.start();
|
||||||
|
|
||||||
|
return job;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
listGateways,
|
listGateways,
|
||||||
registerGateway,
|
registerGateway,
|
||||||
@@ -885,6 +964,7 @@ export const gatewayV2ServiceFactory = ({
|
|||||||
getPAMConnectionDetails,
|
getPAMConnectionDetails,
|
||||||
deleteGatewayById,
|
deleteGatewayById,
|
||||||
heartbeat,
|
heartbeat,
|
||||||
getPamSessionKey
|
getPamSessionKey,
|
||||||
|
initializeHealthcheckNotify
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { GatewaysSchema, TableName, TGateways } from "@app/db/schemas";
|
import { AccessScope, GatewaysSchema, TableName, TGateways } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt } from "@app/lib/knex";
|
import { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt } from "@app/lib/knex";
|
||||||
|
|
||||||
@@ -17,17 +17,14 @@ export const gatewayDALFactory = (db: TDbClient) => {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
.where(buildFindFilter(filter, TableName.Gateway, ["orgId"]))
|
.where(buildFindFilter(filter, TableName.Gateway, ["orgId"]))
|
||||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Gateway}.identityId`)
|
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Gateway}.identityId`)
|
||||||
.join(
|
.join(TableName.Membership, `${TableName.Membership}.actorIdentityId`, `${TableName.Gateway}.identityId`)
|
||||||
TableName.IdentityOrgMembership,
|
|
||||||
`${TableName.IdentityOrgMembership}.identityId`,
|
|
||||||
`${TableName.Gateway}.identityId`
|
|
||||||
)
|
|
||||||
.select(selectAllTableCols(TableName.Gateway))
|
.select(selectAllTableCols(TableName.Gateway))
|
||||||
.select(db.ref("orgId").withSchema(TableName.IdentityOrgMembership).as("identityOrgId"))
|
.select(db.ref("scopeOrgId").withSchema(TableName.Membership).as("identityOrgId"))
|
||||||
.select(db.ref("name").withSchema(TableName.Identity).as("identityName"));
|
.select(db.ref("name").withSchema(TableName.Identity).as("identityName"))
|
||||||
|
.where(`${TableName.Membership}.scope`, AccessScope.Organization);
|
||||||
|
|
||||||
if (filter.orgId) {
|
if (filter.orgId) {
|
||||||
void query.where(`${TableName.IdentityOrgMembership}.orgId`, filter.orgId);
|
void query.where(`${TableName.Membership}.scopeOrgId`, filter.orgId);
|
||||||
}
|
}
|
||||||
if (limit) void query.limit(limit);
|
if (limit) void query.limit(limit);
|
||||||
if (offset) void query.offset(offset);
|
if (offset) void query.offset(offset);
|
||||||
@@ -39,7 +36,7 @@ export const gatewayDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
return docs.map((el) => ({
|
return docs.map((el) => ({
|
||||||
...GatewaysSchema.parse(el),
|
...GatewaysSchema.parse(el),
|
||||||
orgId: el.identityOrgId as string, // todo(daniel): figure out why typescript is not inferring this as a string
|
orgId: el.identityOrgId,
|
||||||
identity: { id: el.identityId, name: el.identityName }
|
identity: { id: el.identityId, name: el.identityName }
|
||||||
}));
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -6,13 +6,15 @@ import { paginateGraphql } from "@octokit/plugin-paginate-graphql";
|
|||||||
import { Octokit as OctokitRest } from "@octokit/rest";
|
import { Octokit as OctokitRest } from "@octokit/rest";
|
||||||
import RE2 from "re2";
|
import RE2 from "re2";
|
||||||
|
|
||||||
import { OrgMembershipRole } from "@app/db/schemas";
|
import { AccessScope, OrgMembershipRole } from "@app/db/schemas";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { groupBy } from "@app/lib/fn";
|
import { groupBy } from "@app/lib/fn";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { retryWithBackoff } from "@app/lib/retry";
|
import { retryWithBackoff } from "@app/lib/retry";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
|
import { TMembershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
||||||
|
import { TMembershipGroupDALFactory } from "@app/services/membership-group/membership-group-dal";
|
||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
||||||
|
|
||||||
import { TGroupDALFactory } from "../group/group-dal";
|
import { TGroupDALFactory } from "../group/group-dal";
|
||||||
@@ -77,11 +79,10 @@ type TGithubOrgSyncServiceFactoryDep = {
|
|||||||
"findGroupMembershipsByUserIdInOrg" | "findGroupMembershipsByGroupIdInOrg" | "insertMany" | "delete"
|
"findGroupMembershipsByUserIdInOrg" | "findGroupMembershipsByGroupIdInOrg" | "insertMany" | "delete"
|
||||||
>;
|
>;
|
||||||
groupDAL: Pick<TGroupDALFactory, "insertMany" | "transaction" | "find">;
|
groupDAL: Pick<TGroupDALFactory, "insertMany" | "transaction" | "find">;
|
||||||
|
membershipRoleDAL: Pick<TMembershipRoleDALFactory, "insertMany">;
|
||||||
|
membershipGroupDAL: Pick<TMembershipGroupDALFactory, "insertMany">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
orgMembershipDAL: Pick<
|
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "findOrgMembershipById" | "findOrgMembershipsWithUsersByOrgId">;
|
||||||
TOrgMembershipDALFactory,
|
|
||||||
"find" | "findOrgMembershipById" | "findOrgMembershipsWithUsersByOrgId"
|
|
||||||
>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGithubOrgSyncServiceFactory = ReturnType<typeof githubOrgSyncServiceFactory>;
|
export type TGithubOrgSyncServiceFactory = ReturnType<typeof githubOrgSyncServiceFactory>;
|
||||||
@@ -93,7 +94,9 @@ export const githubOrgSyncServiceFactory = ({
|
|||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
orgMembershipDAL
|
orgMembershipDAL,
|
||||||
|
membershipRoleDAL,
|
||||||
|
membershipGroupDAL
|
||||||
}: TGithubOrgSyncServiceFactoryDep) => {
|
}: TGithubOrgSyncServiceFactoryDep) => {
|
||||||
const createGithubOrgSync = async ({
|
const createGithubOrgSync = async ({
|
||||||
githubOrgName,
|
githubOrgName,
|
||||||
@@ -368,6 +371,23 @@ export const githubOrgSyncServiceFactory = ({
|
|||||||
})),
|
})),
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
const memberships = await membershipGroupDAL.insertMany(
|
||||||
|
newGroups.map((el) => ({
|
||||||
|
actorGroupId: el.id,
|
||||||
|
scope: AccessScope.Organization,
|
||||||
|
scopeOrgId: orgId
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
await membershipRoleDAL.insertMany(
|
||||||
|
memberships.map((el) => ({
|
||||||
|
membershipId: el.id,
|
||||||
|
role: OrgMembershipRole.Member
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
await userGroupMembershipDAL.insertMany(
|
await userGroupMembershipDAL.insertMany(
|
||||||
newGroups.map((el) => ({
|
newGroups.map((el) => ({
|
||||||
groupId: el.id,
|
groupId: el.id,
|
||||||
@@ -694,6 +714,23 @@ export const githubOrgSyncServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const memberships = await membershipGroupDAL.insertMany(
|
||||||
|
newGroups.map((el) => ({
|
||||||
|
actorGroupId: el.id,
|
||||||
|
scope: AccessScope.Organization,
|
||||||
|
scopeOrgId: orgPermission.orgId
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
await membershipRoleDAL.insertMany(
|
||||||
|
memberships.map((el) => ({
|
||||||
|
membershipId: el.id,
|
||||||
|
role: OrgMembershipRole.Member
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
newGroups.forEach((group) => {
|
newGroups.forEach((group) => {
|
||||||
if (!existingTeamsMap[group.name]) {
|
if (!existingTeamsMap[group.name]) {
|
||||||
existingTeamsMap[group.name] = [];
|
existingTeamsMap[group.name] = [];
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName, TGroups } from "@app/db/schemas";
|
import { AccessScope, TableName, TGroups } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt } from "@app/lib/knex";
|
import { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt } from "@app/lib/knex";
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ export const groupDALFactory = (db: TDbClient) => {
|
|||||||
.select(selectAllTableCols(TableName.Groups));
|
.select(selectAllTableCols(TableName.Groups));
|
||||||
|
|
||||||
if (limit) void query.limit(limit);
|
if (limit) void query.limit(limit);
|
||||||
if (offset) void query.limit(offset);
|
if (offset && offset > 0) void query.offset(offset);
|
||||||
if (sort) {
|
if (sort) {
|
||||||
void query.orderBy(sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls })));
|
void query.orderBy(sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls })));
|
||||||
}
|
}
|
||||||
@@ -36,14 +36,21 @@ export const groupDALFactory = (db: TDbClient) => {
|
|||||||
try {
|
try {
|
||||||
const docs = await (tx || db.replicaNode())(TableName.Groups)
|
const docs = await (tx || db.replicaNode())(TableName.Groups)
|
||||||
.where(`${TableName.Groups}.orgId`, orgId)
|
.where(`${TableName.Groups}.orgId`, orgId)
|
||||||
.leftJoin(TableName.OrgRoles, `${TableName.Groups}.roleId`, `${TableName.OrgRoles}.id`)
|
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||||
|
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||||
|
.join(TableName.Membership, `${TableName.Groups}.id`, `${TableName.Membership}.actorGroupId`)
|
||||||
|
.join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
|
||||||
|
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
|
||||||
.select(selectAllTableCols(TableName.Groups))
|
.select(selectAllTableCols(TableName.Groups))
|
||||||
// cr stands for custom role
|
// cr stands for custom role
|
||||||
.select(db.ref("id").as("crId").withSchema(TableName.OrgRoles))
|
.select(db.ref("id").as("crId").withSchema(TableName.Role))
|
||||||
.select(db.ref("name").as("crName").withSchema(TableName.OrgRoles))
|
.select(db.ref("name").as("crName").withSchema(TableName.Role))
|
||||||
.select(db.ref("slug").as("crSlug").withSchema(TableName.OrgRoles))
|
.select(db.ref("role").withSchema(TableName.MembershipRole))
|
||||||
.select(db.ref("description").as("crDescription").withSchema(TableName.OrgRoles))
|
.select(db.ref("customRoleId").as("roleId").withSchema(TableName.MembershipRole))
|
||||||
.select(db.ref("permissions").as("crPermission").withSchema(TableName.OrgRoles));
|
.select(db.ref("slug").as("crSlug").withSchema(TableName.Role))
|
||||||
|
.select(db.ref("description").as("crDescription").withSchema(TableName.Role))
|
||||||
|
.select(db.ref("permissions").as("crPermission").withSchema(TableName.Role));
|
||||||
|
|
||||||
return docs.map(({ crId, crDescription, crSlug, crPermission, crName, ...el }) => ({
|
return docs.map(({ crId, crDescription, crSlug, crPermission, crName, ...el }) => ({
|
||||||
...el,
|
...el,
|
||||||
customRole: el.roleId
|
customRole: el.roleId
|
||||||
@@ -81,9 +88,11 @@ export const groupDALFactory = (db: TDbClient) => {
|
|||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
const query = db
|
const query = db
|
||||||
.replicaNode()(TableName.OrgMembership)
|
.replicaNode()(TableName.Membership)
|
||||||
.where(`${TableName.OrgMembership}.orgId`, orgId)
|
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||||
|
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||||
|
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||||
.leftJoin(TableName.UserGroupMembership, (bd) => {
|
.leftJoin(TableName.UserGroupMembership, (bd) => {
|
||||||
bd.on(`${TableName.UserGroupMembership}.userId`, "=", `${TableName.Users}.id`).andOn(
|
bd.on(`${TableName.UserGroupMembership}.userId`, "=", `${TableName.Users}.id`).andOn(
|
||||||
`${TableName.UserGroupMembership}.groupId`,
|
`${TableName.UserGroupMembership}.groupId`,
|
||||||
@@ -92,7 +101,7 @@ export const groupDALFactory = (db: TDbClient) => {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
.select(
|
.select(
|
||||||
db.ref("id").withSchema(TableName.OrgMembership),
|
db.ref("id").withSchema(TableName.Membership),
|
||||||
db.ref("groupId").withSchema(TableName.UserGroupMembership),
|
db.ref("groupId").withSchema(TableName.UserGroupMembership),
|
||||||
db.ref("createdAt").withSchema(TableName.UserGroupMembership).as("joinedGroupAt"),
|
db.ref("createdAt").withSchema(TableName.UserGroupMembership).as("joinedGroupAt"),
|
||||||
db.ref("email").withSchema(TableName.Users),
|
db.ref("email").withSchema(TableName.Users),
|
||||||
@@ -160,8 +169,10 @@ export const groupDALFactory = (db: TDbClient) => {
|
|||||||
const findGroupsByProjectId = async (projectId: string, tx?: Knex) => {
|
const findGroupsByProjectId = async (projectId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const docs = await (tx || db.replicaNode())(TableName.Groups)
|
const docs = await (tx || db.replicaNode())(TableName.Groups)
|
||||||
.join(TableName.GroupProjectMembership, `${TableName.Groups}.id`, `${TableName.GroupProjectMembership}.groupId`)
|
.join(TableName.Membership, `${TableName.Membership}.actorGroupId`, `${TableName.Groups}.id`)
|
||||||
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
.where(`${TableName.Membership}.scopeProjectId`, projectId)
|
||||||
|
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||||
|
.whereNotNull(`${TableName.Membership}.actorGroupId`)
|
||||||
.select(selectAllTableCols(TableName.Groups));
|
.select(selectAllTableCols(TableName.Groups));
|
||||||
return docs;
|
return docs;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -172,11 +183,16 @@ export const groupDALFactory = (db: TDbClient) => {
|
|||||||
const findById = async (id: string, tx?: Knex) => {
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await (tx || db.replicaNode())(TableName.Groups)
|
const doc = await (tx || db.replicaNode())(TableName.Groups)
|
||||||
.leftJoin(TableName.OrgRoles, `${TableName.Groups}.roleId`, `${TableName.OrgRoles}.id`)
|
.join(TableName.Membership, `${TableName.Membership}.actorGroupId`, `${TableName.Groups}.id`)
|
||||||
|
.join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
|
||||||
|
.leftJoin(TableName.Role, `${TableName.MembershipRole}.customRoleId`, `${TableName.Role}.id`)
|
||||||
.where(`${TableName.Groups}.id`, id)
|
.where(`${TableName.Groups}.id`, id)
|
||||||
|
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||||
.select(
|
.select(
|
||||||
selectAllTableCols(TableName.Groups),
|
selectAllTableCols(TableName.Groups),
|
||||||
db.ref("slug").as("customRoleSlug").withSchema(TableName.OrgRoles)
|
db.ref("slug").as("customRoleSlug").withSchema(TableName.Role),
|
||||||
|
db.ref("customRoleId").as("roleId").withSchema(TableName.MembershipRole),
|
||||||
|
db.ref("role").withSchema(TableName.MembershipRole)
|
||||||
)
|
)
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
@@ -186,12 +202,36 @@ export const groupDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const findOne = async (filter: Partial<TGroups>, tx?: Knex): Promise<TGroups | undefined> => {
|
||||||
|
try {
|
||||||
|
const doc = await (tx || db.replicaNode())(TableName.Groups)
|
||||||
|
.join(TableName.Membership, `${TableName.Membership}.actorGroupId`, `${TableName.Groups}.id`)
|
||||||
|
.join(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
|
||||||
|
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||||
|
.where((queryBuilder) => {
|
||||||
|
Object.entries(filter).forEach(([key, value]) => {
|
||||||
|
void queryBuilder.where(`${TableName.Groups}.${key}`, value);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.select(
|
||||||
|
selectAllTableCols(TableName.Groups),
|
||||||
|
db.ref("role").withSchema(TableName.MembershipRole),
|
||||||
|
db.ref("customRoleId").as("roleId").withSchema(TableName.MembershipRole)
|
||||||
|
)
|
||||||
|
.first();
|
||||||
|
return doc;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "Find one" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...groupOrm,
|
...groupOrm,
|
||||||
findGroups,
|
findGroups,
|
||||||
findByOrgId,
|
findByOrgId,
|
||||||
findAllGroupPossibleMembers,
|
findAllGroupPossibleMembers,
|
||||||
findGroupsByProjectId,
|
findGroupsByProjectId,
|
||||||
findById
|
findById,
|
||||||
|
findOne
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { ProjectVersion, SecretKeyEncoding, TableName, TUsers } from "@app/db/schemas";
|
import { AccessScope, ProjectVersion, SecretKeyEncoding, TableName, TUsers } from "@app/db/schemas";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError, ScimRequestError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError, ScimRequestError } from "@app/lib/errors";
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ const addAcceptedUsersToGroup = async ({
|
|||||||
group,
|
group,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
groupProjectDAL,
|
membershipGroupDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
@@ -42,13 +42,15 @@ const addAcceptedUsersToGroup = async ({
|
|||||||
const projectIds = Array.from(
|
const projectIds = Array.from(
|
||||||
new Set(
|
new Set(
|
||||||
(
|
(
|
||||||
await groupProjectDAL.find(
|
await membershipGroupDAL.find(
|
||||||
{
|
{
|
||||||
groupId: group.id
|
actorGroupId: group.id,
|
||||||
|
scopeOrgId: group.orgId,
|
||||||
|
scope: AccessScope.Project
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
)
|
)
|
||||||
).map((gp) => gp.projectId)
|
).map((gp) => gp.scopeProjectId as string)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -167,11 +169,11 @@ export const addUsersToGroupByUserIds = async ({
|
|||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
groupProjectDAL,
|
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
tx: outerTx
|
tx: outerTx,
|
||||||
|
membershipGroupDAL
|
||||||
}: TAddUsersToGroupByUserIds) => {
|
}: TAddUsersToGroupByUserIds) => {
|
||||||
const processAddition = async (tx: Knex) => {
|
const processAddition = async (tx: Knex) => {
|
||||||
const foundMembers = await userDAL.find(
|
const foundMembers = await userDAL.find(
|
||||||
@@ -214,15 +216,18 @@ 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,
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: group.orgId,
|
||||||
|
scope: AccessScope.Organization,
|
||||||
$in: {
|
$in: {
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: userIds
|
[`${TableName.Membership}.actorUserId` as "actorUserId"]: userIds
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
);
|
);
|
||||||
|
|
||||||
const existingUserOrgMembershipsUserIdsSet = new Set(existingUserOrgMemberships.map((u) => u.userId));
|
const existingUserOrgMembershipsUserIdsSet = new Set(
|
||||||
|
existingUserOrgMemberships.map((u) => u.actorUserId as string)
|
||||||
|
);
|
||||||
|
|
||||||
userIds.forEach((userId) => {
|
userIds.forEach((userId) => {
|
||||||
if (!existingUserOrgMembershipsUserIdsSet.has(userId))
|
if (!existingUserOrgMembershipsUserIdsSet.has(userId))
|
||||||
@@ -250,7 +255,7 @@ export const addUsersToGroupByUserIds = async ({
|
|||||||
group,
|
group,
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupProjectDAL,
|
membershipGroupDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
@@ -292,9 +297,9 @@ export const removeUsersFromGroupByUserIds = async ({
|
|||||||
userIds,
|
userIds,
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupProjectDAL,
|
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
tx: outerTx
|
tx: outerTx,
|
||||||
|
membershipGroupDAL
|
||||||
}: TRemoveUsersFromGroupByUserIds) => {
|
}: TRemoveUsersFromGroupByUserIds) => {
|
||||||
const processRemoval = async (tx: Knex) => {
|
const processRemoval = async (tx: Knex) => {
|
||||||
const foundMembers = await userDAL.find({
|
const foundMembers = await userDAL.find({
|
||||||
@@ -352,13 +357,15 @@ export const removeUsersFromGroupByUserIds = async ({
|
|||||||
const projectIds = Array.from(
|
const projectIds = Array.from(
|
||||||
new Set(
|
new Set(
|
||||||
(
|
(
|
||||||
await groupProjectDAL.find(
|
await membershipGroupDAL.find(
|
||||||
{
|
{
|
||||||
groupId: group.id
|
scope: AccessScope.Project,
|
||||||
|
actorGroupId: group.id,
|
||||||
|
scopeOrgId: group.orgId
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
)
|
)
|
||||||
).map((gp) => gp.projectId)
|
).map((gp) => gp.scopeProjectId as string)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -422,11 +429,11 @@ export const convertPendingGroupAdditionsToGroupMemberships = async ({
|
|||||||
userIds,
|
userIds,
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupProjectDAL,
|
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
tx: outerTx
|
tx: outerTx,
|
||||||
|
membershipGroupDAL
|
||||||
}: TConvertPendingGroupAdditionsToGroupMemberships) => {
|
}: TConvertPendingGroupAdditionsToGroupMemberships) => {
|
||||||
const processConversion = async (tx: Knex) => {
|
const processConversion = async (tx: Knex) => {
|
||||||
const users = await userDAL.find(
|
const users = await userDAL.find(
|
||||||
@@ -463,7 +470,7 @@ export const convertPendingGroupAdditionsToGroupMemberships = async ({
|
|||||||
group: pendingGroupAddition.group,
|
group: pendingGroupAddition.group,
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupProjectDAL,
|
membershipGroupDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
|
|
||||||
import { OrgMembershipRole, TOrgRoles } from "@app/db/schemas";
|
import { AccessScope, OrgMembershipRole, TRoles } from "@app/db/schemas";
|
||||||
import { TOidcConfigDALFactory } from "@app/ee/services/oidc/oidc-config-dal";
|
import { TOidcConfigDALFactory } from "@app/ee/services/oidc/oidc-config-dal";
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
import { TMembershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
||||||
|
import { TMembershipGroupDALFactory } from "@app/services/membership-group/membership-group-dal";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-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";
|
||||||
@@ -35,8 +36,9 @@ type TGroupServiceFactoryDep = {
|
|||||||
TGroupDALFactory,
|
TGroupDALFactory,
|
||||||
"create" | "findOne" | "update" | "delete" | "findAllGroupPossibleMembers" | "findById" | "transaction"
|
"create" | "findOne" | "update" | "delete" | "findAllGroupPossibleMembers" | "findById" | "transaction"
|
||||||
>;
|
>;
|
||||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
membershipGroupDAL: Pick<TMembershipGroupDALFactory, "find" | "findOne" | "create">;
|
||||||
orgDAL: Pick<TOrgDALFactory, "findMembership" | "countAllOrgMembers">;
|
membershipRoleDAL: Pick<TMembershipRoleDALFactory, "create" | "delete">;
|
||||||
|
orgDAL: Pick<TOrgDALFactory, "findMembership" | "countAllOrgMembers" | "findById">;
|
||||||
userGroupMembershipDAL: Pick<
|
userGroupMembershipDAL: Pick<
|
||||||
TUserGroupMembershipDALFactory,
|
TUserGroupMembershipDALFactory,
|
||||||
"findOne" | "delete" | "filterProjectsByUserMembership" | "transaction" | "insertMany" | "find"
|
"findOne" | "delete" | "filterProjectsByUserMembership" | "transaction" | "insertMany" | "find"
|
||||||
@@ -46,7 +48,7 @@ type TGroupServiceFactoryDep = {
|
|||||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "findLatestProjectKey" | "insertMany">;
|
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "findLatestProjectKey" | "insertMany">;
|
||||||
permissionService: Pick<
|
permissionService: Pick<
|
||||||
TPermissionServiceFactory,
|
TPermissionServiceFactory,
|
||||||
"getOrgPermission" | "getOrgPermissionByRole" | "invalidateProjectPermissionCache"
|
"getOrgPermission" | "getOrgPermissionByRoles" | "invalidateProjectPermissionCache"
|
||||||
>;
|
>;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
oidcConfigDAL: Pick<TOidcConfigDALFactory, "findOne">;
|
oidcConfigDAL: Pick<TOidcConfigDALFactory, "findOne">;
|
||||||
@@ -57,7 +59,6 @@ export type TGroupServiceFactory = ReturnType<typeof groupServiceFactory>;
|
|||||||
export const groupServiceFactory = ({
|
export const groupServiceFactory = ({
|
||||||
userDAL,
|
userDAL,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
groupProjectDAL,
|
|
||||||
orgDAL,
|
orgDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
@@ -65,12 +66,14 @@ export const groupServiceFactory = ({
|
|||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
oidcConfigDAL
|
oidcConfigDAL,
|
||||||
|
membershipGroupDAL,
|
||||||
|
membershipRoleDAL
|
||||||
}: TGroupServiceFactoryDep) => {
|
}: TGroupServiceFactoryDep) => {
|
||||||
const createGroup = async ({ name, slug, role, actor, actorId, actorAuthMethod, actorOrgId }: TCreateGroupDTO) => {
|
const createGroup = async ({ name, slug, role, actor, actorId, actorAuthMethod, actorOrgId }: TCreateGroupDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission, membership } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
@@ -85,25 +88,23 @@ export const groupServiceFactory = ({
|
|||||||
message: "Failed to create group due to plan restriction. Upgrade plan to create group."
|
message: "Failed to create group due to plan restriction. Upgrade plan to create group."
|
||||||
});
|
});
|
||||||
|
|
||||||
const { permission: rolePermission, role: customRole } = await permissionService.getOrgPermissionByRole(
|
const [rolePermissionDetails] = await permissionService.getOrgPermissionByRoles([role], actorOrgId);
|
||||||
role,
|
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(actorOrgId);
|
||||||
actorOrgId
|
const isCustomRole = Boolean(rolePermissionDetails?.role);
|
||||||
);
|
|
||||||
const isCustomRole = Boolean(customRole);
|
|
||||||
if (role !== OrgMembershipRole.NoAccess) {
|
if (role !== OrgMembershipRole.NoAccess) {
|
||||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
shouldUseNewPrivilegeSystem,
|
||||||
OrgPermissionGroupActions.GrantPrivileges,
|
OrgPermissionGroupActions.GrantPrivileges,
|
||||||
OrgPermissionSubjects.Groups,
|
OrgPermissionSubjects.Groups,
|
||||||
permission,
|
permission,
|
||||||
rolePermission
|
rolePermissionDetails.permission
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!permissionBoundary.isValid)
|
if (!permissionBoundary.isValid)
|
||||||
throw new PermissionBoundaryError({
|
throw new PermissionBoundaryError({
|
||||||
message: constructPermissionErrorMessage(
|
message: constructPermissionErrorMessage(
|
||||||
"Failed to create group",
|
"Failed to create group",
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
shouldUseNewPrivilegeSystem,
|
||||||
OrgPermissionGroupActions.GrantPrivileges,
|
OrgPermissionGroupActions.GrantPrivileges,
|
||||||
OrgPermissionSubjects.Groups
|
OrgPermissionSubjects.Groups
|
||||||
),
|
),
|
||||||
@@ -125,7 +126,25 @@ export const groupServiceFactory = ({
|
|||||||
slug: slug || slugify(`${name}-${alphaNumericNanoId(4)}`),
|
slug: slug || slugify(`${name}-${alphaNumericNanoId(4)}`),
|
||||||
orgId: actorOrgId,
|
orgId: actorOrgId,
|
||||||
role: isCustomRole ? OrgMembershipRole.Custom : role,
|
role: isCustomRole ? OrgMembershipRole.Custom : role,
|
||||||
roleId: customRole?.id
|
roleId: null
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
const membership = await membershipGroupDAL.create(
|
||||||
|
{
|
||||||
|
actorGroupId: newGroup.id,
|
||||||
|
scope: AccessScope.Organization,
|
||||||
|
scopeOrgId: actorOrgId
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
await membershipRoleDAL.create(
|
||||||
|
{
|
||||||
|
membershipId: membership.id,
|
||||||
|
role: isCustomRole ? OrgMembershipRole.Custom : role,
|
||||||
|
customRoleId: rolePermissionDetails?.role?.id
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -148,7 +167,7 @@ export const groupServiceFactory = ({
|
|||||||
}: TUpdateGroupDTO) => {
|
}: TUpdateGroupDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission, membership } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
@@ -169,32 +188,31 @@ export const groupServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: `Failed to find group with ID ${id}` });
|
throw new NotFoundError({ message: `Failed to find group with ID ${id}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
let customRole: TOrgRoles | undefined;
|
let customRole: TRoles | undefined;
|
||||||
if (role) {
|
if (role) {
|
||||||
const { permission: rolePermission, role: customOrgRole } = await permissionService.getOrgPermissionByRole(
|
const [rolePermissionDetails] = await permissionService.getOrgPermissionByRoles([role], group.orgId);
|
||||||
role,
|
|
||||||
group.orgId
|
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(actorOrgId);
|
||||||
);
|
const isCustomRole = Boolean(rolePermissionDetails?.role);
|
||||||
|
|
||||||
const isCustomRole = Boolean(customOrgRole);
|
|
||||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
shouldUseNewPrivilegeSystem,
|
||||||
OrgPermissionGroupActions.GrantPrivileges,
|
OrgPermissionGroupActions.GrantPrivileges,
|
||||||
OrgPermissionSubjects.Groups,
|
OrgPermissionSubjects.Groups,
|
||||||
permission,
|
permission,
|
||||||
rolePermission
|
rolePermissionDetails.permission
|
||||||
);
|
);
|
||||||
if (!permissionBoundary.isValid)
|
if (!permissionBoundary.isValid)
|
||||||
throw new PermissionBoundaryError({
|
throw new PermissionBoundaryError({
|
||||||
message: constructPermissionErrorMessage(
|
message: constructPermissionErrorMessage(
|
||||||
"Failed to update group",
|
"Failed to update group",
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
shouldUseNewPrivilegeSystem,
|
||||||
OrgPermissionGroupActions.GrantPrivileges,
|
OrgPermissionGroupActions.GrantPrivileges,
|
||||||
OrgPermissionSubjects.Groups
|
OrgPermissionSubjects.Groups
|
||||||
),
|
),
|
||||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||||
});
|
});
|
||||||
if (isCustomRole) customRole = customOrgRole;
|
if (isCustomRole) customRole = rolePermissionDetails?.role;
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedGroup = await groupDAL.transaction(async (tx) => {
|
const updatedGroup = await groupDAL.transaction(async (tx) => {
|
||||||
@@ -208,35 +226,44 @@ export const groupServiceFactory = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const [updated] = await groupDAL.update(
|
let updated = group;
|
||||||
{
|
|
||||||
id: group.id
|
if (name || slug) {
|
||||||
},
|
[updated] = await groupDAL.update(
|
||||||
{
|
{
|
||||||
name,
|
id: group.id
|
||||||
slug: slug ? slugify(slug) : undefined,
|
},
|
||||||
...(role
|
{
|
||||||
? {
|
name,
|
||||||
role: customRole ? OrgMembershipRole.Custom : role,
|
slug: slug ? slugify(slug) : undefined
|
||||||
roleId: customRole?.id ?? null
|
},
|
||||||
}
|
tx
|
||||||
: {})
|
);
|
||||||
},
|
}
|
||||||
tx
|
|
||||||
);
|
if (role) {
|
||||||
|
const membership = await membershipGroupDAL.findOne(
|
||||||
|
{
|
||||||
|
scope: AccessScope.Organization,
|
||||||
|
actorGroupId: updated.id,
|
||||||
|
scopeOrgId: updated.orgId
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
await membershipRoleDAL.delete({ membershipId: membership.id }, tx);
|
||||||
|
await membershipRoleDAL.create(
|
||||||
|
{
|
||||||
|
membershipId: membership.id,
|
||||||
|
role: customRole ? OrgMembershipRole.Custom : role,
|
||||||
|
customRoleId: customRole?.id ?? null
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return updated;
|
return updated;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (role) {
|
|
||||||
const groupProjects = await groupProjectDAL.find({ groupId: group.id });
|
|
||||||
await Promise.allSettled([
|
|
||||||
...groupProjects.map((groupProject) =>
|
|
||||||
permissionService.invalidateProjectPermissionCache(groupProject.projectId)
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedGroup;
|
return updatedGroup;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -259,17 +286,11 @@ export const groupServiceFactory = ({
|
|||||||
message: "Failed to delete group due to plan restriction. Upgrade plan to delete group."
|
message: "Failed to delete group due to plan restriction. Upgrade plan to delete group."
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupProjects = await groupProjectDAL.find({ groupId: id });
|
|
||||||
|
|
||||||
const [group] = await groupDAL.delete({
|
const [group] = await groupDAL.delete({
|
||||||
id,
|
id,
|
||||||
orgId: actorOrgId
|
orgId: actorOrgId
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.allSettled([
|
|
||||||
...groupProjects.map((groupProject) => permissionService.invalidateProjectPermissionCache(groupProject.projectId))
|
|
||||||
]);
|
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -344,7 +365,7 @@ export const groupServiceFactory = ({
|
|||||||
const addUserToGroup = async ({ id, username, actor, actorId, actorAuthMethod, actorOrgId }: TAddUserToGroupDTO) => {
|
const addUserToGroup = async ({ id, username, actor, actorId, actorAuthMethod, actorOrgId }: TAddUserToGroupDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission, membership } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
@@ -376,22 +397,23 @@ export const groupServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission: groupRolePermission } = await permissionService.getOrgPermissionByRole(group.role, actorOrgId);
|
const [rolePermissionDetails] = await permissionService.getOrgPermissionByRoles([group.role], actorOrgId);
|
||||||
|
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(actorOrgId);
|
||||||
|
|
||||||
// check if user has broader or equal to privileges than group
|
// check if user has broader or equal to privileges than group
|
||||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
shouldUseNewPrivilegeSystem,
|
||||||
OrgPermissionGroupActions.AddMembers,
|
OrgPermissionGroupActions.AddMembers,
|
||||||
OrgPermissionSubjects.Groups,
|
OrgPermissionSubjects.Groups,
|
||||||
permission,
|
permission,
|
||||||
groupRolePermission
|
rolePermissionDetails.permission
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!permissionBoundary.isValid)
|
if (!permissionBoundary.isValid)
|
||||||
throw new PermissionBoundaryError({
|
throw new PermissionBoundaryError({
|
||||||
message: constructPermissionErrorMessage(
|
message: constructPermissionErrorMessage(
|
||||||
"Failed to add user to more privileged group",
|
"Failed to add user to more privileged group",
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
shouldUseNewPrivilegeSystem,
|
||||||
OrgPermissionGroupActions.AddMembers,
|
OrgPermissionGroupActions.AddMembers,
|
||||||
OrgPermissionSubjects.Groups
|
OrgPermissionSubjects.Groups
|
||||||
),
|
),
|
||||||
@@ -410,17 +432,12 @@ export const groupServiceFactory = ({
|
|||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
groupProjectDAL,
|
membershipGroupDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL
|
projectBotDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupProjects = await groupProjectDAL.find({ groupId: group.id });
|
|
||||||
await Promise.allSettled([
|
|
||||||
...groupProjects.map((groupProject) => permissionService.invalidateProjectPermissionCache(groupProject.projectId))
|
|
||||||
]);
|
|
||||||
|
|
||||||
return users[0];
|
return users[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -434,7 +451,7 @@ export const groupServiceFactory = ({
|
|||||||
}: TRemoveUserFromGroupDTO) => {
|
}: TRemoveUserFromGroupDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission, membership } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
@@ -466,21 +483,22 @@ export const groupServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission: groupRolePermission } = await permissionService.getOrgPermissionByRole(group.role, actorOrgId);
|
const [rolePermissionDetails] = await permissionService.getOrgPermissionByRoles([group.role], actorOrgId);
|
||||||
|
const { shouldUseNewPrivilegeSystem } = await orgDAL.findById(actorOrgId);
|
||||||
|
|
||||||
// check if user has broader or equal to privileges than group
|
// check if user has broader or equal to privileges than group
|
||||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
shouldUseNewPrivilegeSystem,
|
||||||
OrgPermissionGroupActions.RemoveMembers,
|
OrgPermissionGroupActions.RemoveMembers,
|
||||||
OrgPermissionSubjects.Groups,
|
OrgPermissionSubjects.Groups,
|
||||||
permission,
|
permission,
|
||||||
groupRolePermission
|
rolePermissionDetails.permission
|
||||||
);
|
);
|
||||||
if (!permissionBoundary.isValid)
|
if (!permissionBoundary.isValid)
|
||||||
throw new PermissionBoundaryError({
|
throw new PermissionBoundaryError({
|
||||||
message: constructPermissionErrorMessage(
|
message: constructPermissionErrorMessage(
|
||||||
"Failed to delete user from more privileged group",
|
"Failed to delete user from more privileged group",
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
shouldUseNewPrivilegeSystem,
|
||||||
OrgPermissionGroupActions.RemoveMembers,
|
OrgPermissionGroupActions.RemoveMembers,
|
||||||
OrgPermissionSubjects.Groups
|
OrgPermissionSubjects.Groups
|
||||||
),
|
),
|
||||||
@@ -498,15 +516,10 @@ export const groupServiceFactory = ({
|
|||||||
userIds: [user.id],
|
userIds: [user.id],
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupProjectDAL,
|
membershipGroupDAL,
|
||||||
projectKeyDAL
|
projectKeyDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const groupProjects = await groupProjectDAL.find({ groupId: group.id });
|
|
||||||
await Promise.allSettled([
|
|
||||||
...groupProjects.map((groupProject) => permissionService.invalidateProjectPermissionCache(groupProject.projectId))
|
|
||||||
]);
|
|
||||||
|
|
||||||
return users[0];
|
return users[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Knex } from "knex";
|
|||||||
import { TGroups } from "@app/db/schemas";
|
import { TGroups } from "@app/db/schemas";
|
||||||
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
|
||||||
import { TGenericPermission } from "@app/lib/types";
|
import { TGenericPermission } from "@app/lib/types";
|
||||||
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
import { TMembershipGroupDALFactory } from "@app/services/membership-group/membership-group-dal";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-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";
|
||||||
@@ -63,7 +63,7 @@ export type TAddUsersToGroup = {
|
|||||||
group: TGroups;
|
group: TGroups;
|
||||||
userDAL: Pick<TUserDALFactory, "findUserEncKeyByUserIdsBatch">;
|
userDAL: Pick<TUserDALFactory, "findUserEncKeyByUserIdsBatch">;
|
||||||
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "find" | "transaction" | "insertMany">;
|
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "find" | "transaction" | "insertMany">;
|
||||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
membershipGroupDAL: Pick<TMembershipGroupDALFactory, "find">;
|
||||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany">;
|
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findById">;
|
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findById">;
|
||||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
||||||
@@ -76,7 +76,7 @@ export type TAddUsersToGroupByUserIds = {
|
|||||||
userDAL: Pick<TUserDALFactory, "find" | "findUserEncKeyByUserIdsBatch" | "transaction">;
|
userDAL: Pick<TUserDALFactory, "find" | "findUserEncKeyByUserIdsBatch" | "transaction">;
|
||||||
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "find" | "transaction" | "insertMany">;
|
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "find" | "transaction" | "insertMany">;
|
||||||
orgDAL: Pick<TOrgDALFactory, "findMembership">;
|
orgDAL: Pick<TOrgDALFactory, "findMembership">;
|
||||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
membershipGroupDAL: Pick<TMembershipGroupDALFactory, "find">;
|
||||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany">;
|
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findById">;
|
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findById">;
|
||||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
||||||
@@ -88,7 +88,7 @@ export type TRemoveUsersFromGroupByUserIds = {
|
|||||||
userIds: string[];
|
userIds: string[];
|
||||||
userDAL: Pick<TUserDALFactory, "find" | "transaction">;
|
userDAL: Pick<TUserDALFactory, "find" | "transaction">;
|
||||||
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "find" | "filterProjectsByUserMembership" | "delete">;
|
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "find" | "filterProjectsByUserMembership" | "delete">;
|
||||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
membershipGroupDAL: Pick<TMembershipGroupDALFactory, "find">;
|
||||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "delete">;
|
projectKeyDAL: Pick<TProjectKeyDALFactory, "delete">;
|
||||||
tx?: Knex;
|
tx?: Knex;
|
||||||
};
|
};
|
||||||
@@ -100,7 +100,7 @@ export type TConvertPendingGroupAdditionsToGroupMemberships = {
|
|||||||
TUserGroupMembershipDALFactory,
|
TUserGroupMembershipDALFactory,
|
||||||
"find" | "transaction" | "insertMany" | "deletePendingUserGroupMembershipsByUserIds"
|
"find" | "transaction" | "insertMany" | "deletePendingUserGroupMembershipsByUserIds"
|
||||||
>;
|
>;
|
||||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
membershipGroupDAL: Pick<TMembershipGroupDALFactory, "find">;
|
||||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany">;
|
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findById">;
|
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findById">;
|
||||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName, TUserEncryptionKeys } from "@app/db/schemas";
|
import { AccessScope, TableName, TUserEncryptionKeys } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify } from "@app/lib/knex";
|
import { ormify } from "@app/lib/knex";
|
||||||
|
|
||||||
@@ -18,21 +18,19 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
*/
|
*/
|
||||||
const filterProjectsByUserMembership = async (userId: string, groupId: string, projectIds: string[], tx?: Knex) => {
|
const filterProjectsByUserMembership = async (userId: string, groupId: string, projectIds: string[], tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const userProjectMemberships: string[] = await (tx || db.replicaNode())(TableName.ProjectMembership)
|
const userProjectMemberships: string[] = await (tx || db.replicaNode())(TableName.Membership)
|
||||||
.where(`${TableName.ProjectMembership}.userId`, userId)
|
.where(`${TableName.Membership}.actorUserId`, userId)
|
||||||
.whereIn(`${TableName.ProjectMembership}.projectId`, projectIds)
|
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||||
.pluck(`${TableName.ProjectMembership}.projectId`);
|
.whereIn(`${TableName.Membership}.scopeProjectId`, projectIds)
|
||||||
|
.pluck(`${TableName.Membership}.scopeProjectId`);
|
||||||
|
|
||||||
const userGroupMemberships: string[] = await (tx || db.replicaNode())(TableName.UserGroupMembership)
|
const userGroupMemberships: string[] = await (tx || db.replicaNode())(TableName.UserGroupMembership)
|
||||||
.where(`${TableName.UserGroupMembership}.userId`, userId)
|
.where(`${TableName.UserGroupMembership}.userId`, userId)
|
||||||
.whereNot(`${TableName.UserGroupMembership}.groupId`, groupId)
|
.whereNot(`${TableName.UserGroupMembership}.groupId`, groupId)
|
||||||
.join(
|
.join(TableName.Membership, `${TableName.UserGroupMembership}.groupId`, `${TableName.Membership}.actorGroupId`)
|
||||||
TableName.GroupProjectMembership,
|
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||||
`${TableName.UserGroupMembership}.groupId`,
|
.whereIn(`${TableName.Membership}.scopeProjectId`, projectIds)
|
||||||
`${TableName.GroupProjectMembership}.groupId`
|
.pluck(`${TableName.Membership}.scopeProjectId`);
|
||||||
)
|
|
||||||
.whereIn(`${TableName.GroupProjectMembership}.projectId`, projectIds)
|
|
||||||
.pluck(`${TableName.GroupProjectMembership}.projectId`);
|
|
||||||
|
|
||||||
return new Set(userProjectMemberships.concat(userGroupMemberships));
|
return new Set(userProjectMemberships.concat(userGroupMemberships));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -44,13 +42,10 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
const findUserGroupMembershipsInProject = async (usernames: string[], projectId: string, tx?: Knex) => {
|
const findUserGroupMembershipsInProject = async (usernames: string[], projectId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const usernameDocs: string[] = await (tx || db.replicaNode())(TableName.UserGroupMembership)
|
const usernameDocs: string[] = await (tx || db.replicaNode())(TableName.UserGroupMembership)
|
||||||
.join(
|
.join(TableName.Membership, `${TableName.UserGroupMembership}.groupId`, `${TableName.Membership}.actorGroupId`)
|
||||||
TableName.GroupProjectMembership,
|
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||||
`${TableName.UserGroupMembership}.groupId`,
|
|
||||||
`${TableName.GroupProjectMembership}.groupId`
|
|
||||||
)
|
|
||||||
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
||||||
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
.where(`${TableName.Membership}.scopeProjectId`, projectId)
|
||||||
.whereIn(`${TableName.Users}.username`, usernames)
|
.whereIn(`${TableName.Users}.username`, usernames)
|
||||||
.pluck(`${TableName.Users}.id`);
|
.pluck(`${TableName.Users}.id`);
|
||||||
|
|
||||||
@@ -73,24 +68,25 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
try {
|
try {
|
||||||
// get list of groups in the project with id [projectId]
|
// get list of groups in the project with id [projectId]
|
||||||
// that that are not the group with id [groupId]
|
// that that are not the group with id [groupId]
|
||||||
const groups: string[] = await (tx || db.replicaNode())(TableName.GroupProjectMembership)
|
const groups: string[] = await (tx || db.replicaNode())(TableName.Membership)
|
||||||
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
.where(`${TableName.Membership}.scopeProjectId`, projectId)
|
||||||
.whereNot(`${TableName.GroupProjectMembership}.groupId`, groupId)
|
.whereNot(`${TableName.Membership}.actorGroupId`, groupId)
|
||||||
.pluck(`${TableName.GroupProjectMembership}.groupId`);
|
.pluck(`${TableName.Membership}.actorGroupId`);
|
||||||
|
|
||||||
// main query
|
// main query
|
||||||
const members = await (tx || db.replicaNode())(TableName.UserGroupMembership)
|
const members = await (tx || db.replicaNode())(TableName.UserGroupMembership)
|
||||||
.where(`${TableName.UserGroupMembership}.groupId`, groupId)
|
.where(`${TableName.UserGroupMembership}.groupId`, groupId)
|
||||||
.where(`${TableName.UserGroupMembership}.isPending`, false)
|
.where(`${TableName.UserGroupMembership}.isPending`, false)
|
||||||
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
||||||
.leftJoin(TableName.ProjectMembership, (bd) => {
|
.leftJoin(TableName.Membership, (bd) => {
|
||||||
bd.on(`${TableName.Users}.id`, "=", `${TableName.ProjectMembership}.userId`).andOn(
|
bd.on(`${TableName.Users}.id`, "=", `${TableName.Membership}.actorUserId`).andOn(
|
||||||
`${TableName.ProjectMembership}.projectId`,
|
`${TableName.Membership}.scopeProjectId`,
|
||||||
"=",
|
"=",
|
||||||
db.raw("?", [projectId])
|
db.raw("?", [projectId])
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.whereNull(`${TableName.ProjectMembership}.userId`)
|
.whereNull(`${TableName.Membership}.actorUserId`)
|
||||||
|
.where(`${TableName.Membership}.scope`, AccessScope.Project)
|
||||||
.leftJoin<TUserEncryptionKeys>(
|
.leftJoin<TUserEncryptionKeys>(
|
||||||
TableName.UserEncryptionKey,
|
TableName.UserEncryptionKey,
|
||||||
`${TableName.UserEncryptionKey}.userId`,
|
`${TableName.UserEncryptionKey}.userId`,
|
||||||
@@ -166,15 +162,17 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
const docs = await db
|
const docs = await db
|
||||||
.replicaNode()(TableName.UserGroupMembership)
|
.replicaNode()(TableName.UserGroupMembership)
|
||||||
.join(TableName.Groups, `${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.id`)
|
.join(TableName.Groups, `${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.id`)
|
||||||
.join(TableName.OrgMembership, `${TableName.UserGroupMembership}.userId`, `${TableName.OrgMembership}.userId`)
|
.join(TableName.Membership, `${TableName.UserGroupMembership}.userId`, `${TableName.Membership}.actorUserId`)
|
||||||
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
||||||
.where(`${TableName.UserGroupMembership}.userId`, userId)
|
.where(`${TableName.UserGroupMembership}.userId`, userId)
|
||||||
|
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||||
|
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||||
.where(`${TableName.Groups}.orgId`, orgId)
|
.where(`${TableName.Groups}.orgId`, orgId)
|
||||||
.select(
|
.select(
|
||||||
db.ref("id").withSchema(TableName.UserGroupMembership),
|
db.ref("id").withSchema(TableName.UserGroupMembership),
|
||||||
db.ref("groupId").withSchema(TableName.UserGroupMembership),
|
db.ref("groupId").withSchema(TableName.UserGroupMembership),
|
||||||
db.ref("name").withSchema(TableName.Groups).as("groupName"),
|
db.ref("name").withSchema(TableName.Groups).as("groupName"),
|
||||||
db.ref("id").withSchema(TableName.OrgMembership).as("orgMembershipId"),
|
db.ref("id").withSchema(TableName.Membership).as("orgMembershipId"),
|
||||||
db.ref("firstName").withSchema(TableName.Users).as("firstName"),
|
db.ref("firstName").withSchema(TableName.Users).as("firstName"),
|
||||||
db.ref("lastName").withSchema(TableName.Users).as("lastName"),
|
db.ref("lastName").withSchema(TableName.Users).as("lastName"),
|
||||||
db.ref("slug").withSchema(TableName.Groups).as("groupSlug")
|
db.ref("slug").withSchema(TableName.Groups).as("groupSlug")
|
||||||
@@ -191,15 +189,17 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
const docs = await db
|
const docs = await db
|
||||||
.replicaNode()(TableName.UserGroupMembership)
|
.replicaNode()(TableName.UserGroupMembership)
|
||||||
.join(TableName.Groups, `${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.id`)
|
.join(TableName.Groups, `${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.id`)
|
||||||
.join(TableName.OrgMembership, `${TableName.UserGroupMembership}.userId`, `${TableName.OrgMembership}.userId`)
|
.join(TableName.Membership, `${TableName.UserGroupMembership}.userId`, `${TableName.Membership}.actorUserId`)
|
||||||
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
|
||||||
.where(`${TableName.Groups}.id`, groupId)
|
.where(`${TableName.Groups}.id`, groupId)
|
||||||
|
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
|
||||||
|
.where(`${TableName.Membership}.scopeOrgId`, orgId)
|
||||||
.where(`${TableName.Groups}.orgId`, orgId)
|
.where(`${TableName.Groups}.orgId`, orgId)
|
||||||
.select(
|
.select(
|
||||||
db.ref("id").withSchema(TableName.UserGroupMembership),
|
db.ref("id").withSchema(TableName.UserGroupMembership),
|
||||||
db.ref("groupId").withSchema(TableName.UserGroupMembership),
|
db.ref("groupId").withSchema(TableName.UserGroupMembership),
|
||||||
db.ref("name").withSchema(TableName.Groups).as("groupName"),
|
db.ref("name").withSchema(TableName.Groups).as("groupName"),
|
||||||
db.ref("id").withSchema(TableName.OrgMembership).as("orgMembershipId"),
|
db.ref("id").withSchema(TableName.Membership).as("orgMembershipId"),
|
||||||
db.ref("firstName").withSchema(TableName.Users).as("firstName"),
|
db.ref("firstName").withSchema(TableName.Users).as("firstName"),
|
||||||
db.ref("lastName").withSchema(TableName.Users).as("lastName")
|
db.ref("lastName").withSchema(TableName.Users).as("lastName")
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import { TDbClient } from "@app/db";
|
|
||||||
import { TableName } from "@app/db/schemas";
|
|
||||||
import { ormify } from "@app/lib/knex";
|
|
||||||
|
|
||||||
export type TIdentityProjectAdditionalPrivilegeV2DALFactory = ReturnType<
|
|
||||||
typeof identityProjectAdditionalPrivilegeV2DALFactory
|
|
||||||
>;
|
|
||||||
|
|
||||||
export const identityProjectAdditionalPrivilegeV2DALFactory = (db: TDbClient) => {
|
|
||||||
const orm = ormify(db, TableName.IdentityProjectAdditionalPrivilege);
|
|
||||||
return orm;
|
|
||||||
};
|
|
||||||
@@ -1,434 +0,0 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
|
||||||
import { packRules } from "@casl/ability/extra";
|
|
||||||
|
|
||||||
import { ActionProjectType, TableName } from "@app/db/schemas";
|
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
|
||||||
import { ms } from "@app/lib/ms";
|
|
||||||
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
|
||||||
import { unpackPermissions } from "@app/server/routes/sanitizedSchema/permission";
|
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
|
||||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
|
||||||
|
|
||||||
import { constructPermissionErrorMessage, validatePrivilegeChangeOperation } from "../permission/permission-fns";
|
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service-types";
|
|
||||||
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "../permission/project-permission";
|
|
||||||
import { TIdentityProjectAdditionalPrivilegeV2DALFactory } from "./identity-project-additional-privilege-v2-dal";
|
|
||||||
import {
|
|
||||||
IdentityProjectAdditionalPrivilegeTemporaryMode,
|
|
||||||
TCreateIdentityPrivilegeDTO,
|
|
||||||
TDeleteIdentityPrivilegeByIdDTO,
|
|
||||||
TGetIdentityPrivilegeDetailsByIdDTO,
|
|
||||||
TGetIdentityPrivilegeDetailsBySlugDTO,
|
|
||||||
TListIdentityPrivilegesDTO,
|
|
||||||
TUpdateIdentityPrivilegeByIdDTO
|
|
||||||
} from "./identity-project-additional-privilege-v2-types";
|
|
||||||
|
|
||||||
type TIdentityProjectAdditionalPrivilegeV2ServiceFactoryDep = {
|
|
||||||
identityProjectAdditionalPrivilegeDAL: TIdentityProjectAdditionalPrivilegeV2DALFactory;
|
|
||||||
identityProjectDAL: Pick<TIdentityProjectDALFactory, "findOne" | "findById">;
|
|
||||||
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
|
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "invalidateProjectPermissionCache">;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TIdentityProjectAdditionalPrivilegeV2ServiceFactory = ReturnType<
|
|
||||||
typeof identityProjectAdditionalPrivilegeV2ServiceFactory
|
|
||||||
>;
|
|
||||||
|
|
||||||
export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|
||||||
identityProjectAdditionalPrivilegeDAL,
|
|
||||||
identityProjectDAL,
|
|
||||||
projectDAL,
|
|
||||||
permissionService
|
|
||||||
}: TIdentityProjectAdditionalPrivilegeV2ServiceFactoryDep) => {
|
|
||||||
const create = async ({
|
|
||||||
slug,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId,
|
|
||||||
actorOrgId,
|
|
||||||
identityId,
|
|
||||||
permissions: customPermission,
|
|
||||||
actorAuthMethod,
|
|
||||||
...dto
|
|
||||||
}: TCreateIdentityPrivilegeDTO) => {
|
|
||||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
|
||||||
if (!identityProjectMembership)
|
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
ProjectPermissionIdentityActions.Edit,
|
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
|
||||||
);
|
|
||||||
const { permission: targetIdentityPermission, membership } = await permissionService.getProjectPermission({
|
|
||||||
actor: ActorType.IDENTITY,
|
|
||||||
actorId: identityId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
|
|
||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
|
||||||
targetIdentityPermission.update(targetIdentityPermission.rules.concat(customPermission));
|
|
||||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Identity,
|
|
||||||
permission,
|
|
||||||
targetIdentityPermission
|
|
||||||
);
|
|
||||||
if (!permissionBoundary.isValid)
|
|
||||||
throw new PermissionBoundaryError({
|
|
||||||
message: constructPermissionErrorMessage(
|
|
||||||
"Failed to update more privileged identity",
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Identity
|
|
||||||
),
|
|
||||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
|
||||||
});
|
|
||||||
validateHandlebarTemplate("Identity Additional Privilege Create", JSON.stringify(customPermission || []), {
|
|
||||||
allowedExpressions: (val) => val.includes("identity.")
|
|
||||||
});
|
|
||||||
|
|
||||||
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
|
|
||||||
slug,
|
|
||||||
projectMembershipId: identityProjectMembership.id
|
|
||||||
});
|
|
||||||
if (existingSlug) throw new BadRequestError({ message: "Additional privilege with provided slug already exists" });
|
|
||||||
|
|
||||||
const packedPermission = JSON.stringify(packRules(customPermission));
|
|
||||||
if (!dto.isTemporary) {
|
|
||||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
|
|
||||||
projectMembershipId: identityProjectMembership.id,
|
|
||||||
slug,
|
|
||||||
permissions: packedPermission
|
|
||||||
});
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(identityProjectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
|
|
||||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
|
|
||||||
projectMembershipId: identityProjectMembership.id,
|
|
||||||
slug,
|
|
||||||
permissions: packedPermission,
|
|
||||||
isTemporary: true,
|
|
||||||
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative,
|
|
||||||
temporaryRange: dto.temporaryRange,
|
|
||||||
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
|
|
||||||
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
|
|
||||||
});
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(identityProjectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateById = async ({
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
actorOrgId,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod
|
|
||||||
}: TUpdateIdentityPrivilegeByIdDTO) => {
|
|
||||||
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findById(id);
|
|
||||||
if (!identityPrivilege) throw new NotFoundError({ message: `Identity privilege with ${id} not found` });
|
|
||||||
|
|
||||||
const identityProjectMembership = await identityProjectDAL.findOne({ id: identityPrivilege.projectMembershipId });
|
|
||||||
if (!identityProjectMembership)
|
|
||||||
throw new NotFoundError({
|
|
||||||
message: `Failed to find identity with membership ${identityPrivilege.projectMembershipId}`
|
|
||||||
});
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
ProjectPermissionIdentityActions.Edit,
|
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
|
||||||
);
|
|
||||||
const { permission: targetIdentityPermission, membership } = await permissionService.getProjectPermission({
|
|
||||||
actor: ActorType.IDENTITY,
|
|
||||||
actorId: identityProjectMembership.identityId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
|
|
||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
|
||||||
targetIdentityPermission.update(targetIdentityPermission.rules.concat(data.permissions || []));
|
|
||||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Identity,
|
|
||||||
permission,
|
|
||||||
targetIdentityPermission
|
|
||||||
);
|
|
||||||
if (!permissionBoundary.isValid)
|
|
||||||
throw new PermissionBoundaryError({
|
|
||||||
message: constructPermissionErrorMessage(
|
|
||||||
"Failed to update more privileged identity",
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Identity
|
|
||||||
),
|
|
||||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
|
||||||
});
|
|
||||||
|
|
||||||
validateHandlebarTemplate("Identity Additional Privilege Update", JSON.stringify(data.permissions || []), {
|
|
||||||
allowedExpressions: (val) => val.includes("identity.")
|
|
||||||
});
|
|
||||||
|
|
||||||
if (data?.slug) {
|
|
||||||
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
|
|
||||||
slug: data.slug,
|
|
||||||
projectMembershipId: identityProjectMembership.id
|
|
||||||
});
|
|
||||||
if (existingSlug && existingSlug.id !== identityPrivilege.id)
|
|
||||||
throw new BadRequestError({ message: "Additional privilege with provided slug already exists" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const isTemporary = typeof data?.isTemporary !== "undefined" ? data.isTemporary : identityPrivilege.isTemporary;
|
|
||||||
const packedPermission = data.permissions ? JSON.stringify(packRules(data.permissions)) : undefined;
|
|
||||||
if (isTemporary) {
|
|
||||||
const temporaryAccessStartTime = data?.temporaryAccessStartTime || identityPrivilege?.temporaryAccessStartTime;
|
|
||||||
const temporaryRange = data?.temporaryRange || identityPrivilege?.temporaryRange;
|
|
||||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
|
|
||||||
slug: data.slug,
|
|
||||||
permissions: packedPermission,
|
|
||||||
isTemporary: data.isTemporary,
|
|
||||||
temporaryRange: data.temporaryRange,
|
|
||||||
temporaryMode: data.temporaryMode,
|
|
||||||
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
|
|
||||||
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
|
|
||||||
});
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(identityProjectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
|
|
||||||
slug: data.slug,
|
|
||||||
permissions: packedPermission,
|
|
||||||
isTemporary: false,
|
|
||||||
temporaryAccessStartTime: null,
|
|
||||||
temporaryAccessEndTime: null,
|
|
||||||
temporaryRange: null,
|
|
||||||
temporaryMode: null
|
|
||||||
});
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(identityProjectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteById = async ({ actorId, id, actor, actorOrgId, actorAuthMethod }: TDeleteIdentityPrivilegeByIdDTO) => {
|
|
||||||
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findById(id);
|
|
||||||
if (!identityPrivilege) throw new NotFoundError({ message: `Identity privilege with ${id} not found` });
|
|
||||||
|
|
||||||
const identityProjectMembership = await identityProjectDAL.findOne({ id: identityPrivilege.projectMembershipId });
|
|
||||||
if (!identityProjectMembership)
|
|
||||||
throw new NotFoundError({
|
|
||||||
message: `Failed to find identity with membership ${identityPrivilege.projectMembershipId}`
|
|
||||||
});
|
|
||||||
|
|
||||||
const { permission, membership } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
ProjectPermissionIdentityActions.Edit,
|
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
|
||||||
);
|
|
||||||
const { permission: identityRolePermission } = await permissionService.getProjectPermission({
|
|
||||||
actor: ActorType.IDENTITY,
|
|
||||||
actorId: identityProjectMembership.identityId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Identity,
|
|
||||||
permission,
|
|
||||||
identityRolePermission
|
|
||||||
);
|
|
||||||
if (!permissionBoundary.isValid)
|
|
||||||
throw new PermissionBoundaryError({
|
|
||||||
message: constructPermissionErrorMessage(
|
|
||||||
"Failed to update more privileged identity",
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Identity
|
|
||||||
),
|
|
||||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
|
||||||
});
|
|
||||||
|
|
||||||
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(identityProjectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...deletedPrivilege,
|
|
||||||
permissions: unpackPermissions(deletedPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPrivilegeDetailsById = async ({
|
|
||||||
id,
|
|
||||||
actorOrgId,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod
|
|
||||||
}: TGetIdentityPrivilegeDetailsByIdDTO) => {
|
|
||||||
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findById(id);
|
|
||||||
if (!identityPrivilege) throw new NotFoundError({ message: `Identity privilege with ${id} not found` });
|
|
||||||
|
|
||||||
const identityProjectMembership = await identityProjectDAL.findOne({ id: identityPrivilege.projectMembershipId });
|
|
||||||
if (!identityProjectMembership)
|
|
||||||
throw new NotFoundError({
|
|
||||||
message: `Failed to find identity with membership ${identityPrivilege.projectMembershipId}`
|
|
||||||
});
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
ProjectPermissionIdentityActions.Read,
|
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...identityPrivilege,
|
|
||||||
permissions: unpackPermissions(identityPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPrivilegeDetailsBySlug = async ({
|
|
||||||
identityId,
|
|
||||||
slug,
|
|
||||||
projectSlug,
|
|
||||||
actorOrgId,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod
|
|
||||||
}: TGetIdentityPrivilegeDetailsBySlugDTO) => {
|
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug ${slug} not found` });
|
|
||||||
const projectId = project.id;
|
|
||||||
|
|
||||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
|
||||||
if (!identityProjectMembership)
|
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
ProjectPermissionIdentityActions.Read,
|
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
|
||||||
);
|
|
||||||
|
|
||||||
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findOne({
|
|
||||||
slug,
|
|
||||||
projectMembershipId: identityProjectMembership.id
|
|
||||||
});
|
|
||||||
if (!identityPrivilege) throw new NotFoundError({ message: "Identity additional privilege not found" });
|
|
||||||
|
|
||||||
return {
|
|
||||||
...identityPrivilege,
|
|
||||||
permissions: unpackPermissions(identityPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const listIdentityProjectPrivileges = async ({
|
|
||||||
identityId,
|
|
||||||
actorOrgId,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod,
|
|
||||||
projectId
|
|
||||||
}: TListIdentityPrivilegesDTO) => {
|
|
||||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
|
||||||
if (!identityProjectMembership)
|
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
ProjectPermissionIdentityActions.Read,
|
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
|
||||||
);
|
|
||||||
|
|
||||||
const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find(
|
|
||||||
{
|
|
||||||
projectMembershipId: identityProjectMembership.id
|
|
||||||
},
|
|
||||||
{ sort: [[`${TableName.IdentityProjectAdditionalPrivilege}.slug` as "slug", "asc"]] }
|
|
||||||
);
|
|
||||||
return identityPrivileges;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
getPrivilegeDetailsById,
|
|
||||||
getPrivilegeDetailsBySlug,
|
|
||||||
listIdentityProjectPrivileges,
|
|
||||||
create,
|
|
||||||
updateById,
|
|
||||||
deleteById
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import { TProjectPermission } from "@app/lib/types";
|
|
||||||
|
|
||||||
import { TProjectPermissionV2Schema } from "../permission/project-permission";
|
|
||||||
|
|
||||||
export enum IdentityProjectAdditionalPrivilegeTemporaryMode {
|
|
||||||
Relative = "relative"
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TCreateIdentityPrivilegeDTO = {
|
|
||||||
permissions: TProjectPermissionV2Schema[];
|
|
||||||
identityId: string;
|
|
||||||
projectId: string;
|
|
||||||
slug: string;
|
|
||||||
} & (
|
|
||||||
| {
|
|
||||||
isTemporary: false;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
isTemporary: true;
|
|
||||||
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative;
|
|
||||||
temporaryRange: string;
|
|
||||||
temporaryAccessStartTime: string;
|
|
||||||
}
|
|
||||||
) &
|
|
||||||
Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TUpdateIdentityPrivilegeByIdDTO = { id: string } & Omit<TProjectPermission, "projectId"> & {
|
|
||||||
data: Partial<{
|
|
||||||
permissions: TProjectPermissionV2Schema[];
|
|
||||||
slug: string;
|
|
||||||
isTemporary: boolean;
|
|
||||||
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative;
|
|
||||||
temporaryRange: string;
|
|
||||||
temporaryAccessStartTime: string;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TDeleteIdentityPrivilegeByIdDTO = Omit<TProjectPermission, "projectId"> & {
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TGetIdentityPrivilegeDetailsByIdDTO = Omit<TProjectPermission, "projectId"> & {
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TListIdentityPrivilegesDTO = Omit<TProjectPermission, "projectId"> & {
|
|
||||||
identityId: string;
|
|
||||||
projectId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TGetIdentityPrivilegeDetailsBySlugDTO = Omit<TProjectPermission, "projectId"> & {
|
|
||||||
slug: string;
|
|
||||||
identityId: string;
|
|
||||||
projectSlug: string;
|
|
||||||
};
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import { TDbClient } from "@app/db";
|
|
||||||
import { TableName } from "@app/db/schemas";
|
|
||||||
import { ormify } from "@app/lib/knex";
|
|
||||||
|
|
||||||
export type TIdentityProjectAdditionalPrivilegeDALFactory = ReturnType<
|
|
||||||
typeof identityProjectAdditionalPrivilegeDALFactory
|
|
||||||
>;
|
|
||||||
|
|
||||||
export const identityProjectAdditionalPrivilegeDALFactory = (db: TDbClient) => {
|
|
||||||
const orm = ormify(db, TableName.IdentityProjectAdditionalPrivilege);
|
|
||||||
return orm;
|
|
||||||
};
|
|
||||||
@@ -1,451 +0,0 @@
|
|||||||
import { ForbiddenError, MongoAbility, RawRuleOf, subject } from "@casl/ability";
|
|
||||||
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
|
||||||
import { ms } from "@app/lib/ms";
|
|
||||||
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
|
||||||
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
|
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
|
||||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
|
||||||
|
|
||||||
import { constructPermissionErrorMessage, validatePrivilegeChangeOperation } from "../permission/permission-fns";
|
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service-types";
|
|
||||||
import {
|
|
||||||
ProjectPermissionIdentityActions,
|
|
||||||
ProjectPermissionSet,
|
|
||||||
ProjectPermissionSub
|
|
||||||
} from "../permission/project-permission";
|
|
||||||
import { TIdentityProjectAdditionalPrivilegeDALFactory } from "./identity-project-additional-privilege-dal";
|
|
||||||
import {
|
|
||||||
IdentityProjectAdditionalPrivilegeTemporaryMode,
|
|
||||||
TCreateIdentityPrivilegeDTO,
|
|
||||||
TDeleteIdentityPrivilegeDTO,
|
|
||||||
TGetIdentityPrivilegeDetailsDTO,
|
|
||||||
TListIdentityPrivilegesDTO,
|
|
||||||
TUpdateIdentityPrivilegeDTO
|
|
||||||
} from "./identity-project-additional-privilege-types";
|
|
||||||
|
|
||||||
type TIdentityProjectAdditionalPrivilegeServiceFactoryDep = {
|
|
||||||
identityProjectAdditionalPrivilegeDAL: TIdentityProjectAdditionalPrivilegeDALFactory;
|
|
||||||
identityProjectDAL: Pick<TIdentityProjectDALFactory, "findOne" | "findById">;
|
|
||||||
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
|
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "invalidateProjectPermissionCache">;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TIdentityProjectAdditionalPrivilegeServiceFactory = ReturnType<
|
|
||||||
typeof identityProjectAdditionalPrivilegeServiceFactory
|
|
||||||
>;
|
|
||||||
|
|
||||||
const unpackPermissions = (permissions: unknown) =>
|
|
||||||
UnpackedPermissionSchema.array().parse(
|
|
||||||
unpackRules((permissions || []) as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[])
|
|
||||||
);
|
|
||||||
|
|
||||||
export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|
||||||
identityProjectAdditionalPrivilegeDAL,
|
|
||||||
identityProjectDAL,
|
|
||||||
permissionService,
|
|
||||||
projectDAL
|
|
||||||
}: TIdentityProjectAdditionalPrivilegeServiceFactoryDep) => {
|
|
||||||
const create = async ({
|
|
||||||
slug,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
identityId,
|
|
||||||
projectSlug,
|
|
||||||
permissions: customPermission,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod,
|
|
||||||
...dto
|
|
||||||
}: TCreateIdentityPrivilegeDTO) => {
|
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
|
||||||
const projectId = project.id;
|
|
||||||
|
|
||||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
|
||||||
if (!identityProjectMembership)
|
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
|
||||||
|
|
||||||
const { permission, membership } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
ProjectPermissionIdentityActions.Edit,
|
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
|
||||||
);
|
|
||||||
|
|
||||||
const { permission: targetIdentityPermission } = await permissionService.getProjectPermission({
|
|
||||||
actor: ActorType.IDENTITY,
|
|
||||||
actorId: identityId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
|
|
||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
|
||||||
targetIdentityPermission.update(targetIdentityPermission.rules.concat(customPermission));
|
|
||||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Identity,
|
|
||||||
permission,
|
|
||||||
targetIdentityPermission
|
|
||||||
);
|
|
||||||
if (!permissionBoundary.isValid)
|
|
||||||
throw new PermissionBoundaryError({
|
|
||||||
message: constructPermissionErrorMessage(
|
|
||||||
"Failed to update more privileged identity",
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Identity
|
|
||||||
),
|
|
||||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
|
||||||
});
|
|
||||||
|
|
||||||
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
|
|
||||||
slug,
|
|
||||||
projectMembershipId: identityProjectMembership.id
|
|
||||||
});
|
|
||||||
if (existingSlug) throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
|
|
||||||
|
|
||||||
validateHandlebarTemplate("Identity Additional Privilege Create", JSON.stringify(customPermission || []), {
|
|
||||||
allowedExpressions: (val) => val.includes("identity.")
|
|
||||||
});
|
|
||||||
|
|
||||||
const packedPermission = JSON.stringify(packRules(customPermission));
|
|
||||||
if (!dto.isTemporary) {
|
|
||||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
|
|
||||||
projectMembershipId: identityProjectMembership.id,
|
|
||||||
slug,
|
|
||||||
permissions: packedPermission
|
|
||||||
});
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(identityProjectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
|
|
||||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
|
|
||||||
projectMembershipId: identityProjectMembership.id,
|
|
||||||
slug,
|
|
||||||
permissions: packedPermission,
|
|
||||||
isTemporary: true,
|
|
||||||
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative,
|
|
||||||
temporaryRange: dto.temporaryRange,
|
|
||||||
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
|
|
||||||
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
|
|
||||||
});
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(identityProjectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateBySlug = async ({
|
|
||||||
projectSlug,
|
|
||||||
slug,
|
|
||||||
identityId,
|
|
||||||
data,
|
|
||||||
actorOrgId,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod
|
|
||||||
}: TUpdateIdentityPrivilegeDTO) => {
|
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
|
||||||
const projectId = project.id;
|
|
||||||
|
|
||||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
|
||||||
if (!identityProjectMembership)
|
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
|
||||||
|
|
||||||
const { permission, membership } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
ProjectPermissionIdentityActions.Edit,
|
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
|
||||||
);
|
|
||||||
|
|
||||||
const { permission: targetIdentityPermission } = await permissionService.getProjectPermission({
|
|
||||||
actor: ActorType.IDENTITY,
|
|
||||||
actorId: identityProjectMembership.identityId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
|
|
||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
|
||||||
targetIdentityPermission.update(targetIdentityPermission.rules.concat(data.permissions || []));
|
|
||||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Identity,
|
|
||||||
permission,
|
|
||||||
targetIdentityPermission
|
|
||||||
);
|
|
||||||
if (!permissionBoundary.isValid)
|
|
||||||
throw new PermissionBoundaryError({
|
|
||||||
message: constructPermissionErrorMessage(
|
|
||||||
"Failed to update more privileged identity",
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Identity
|
|
||||||
),
|
|
||||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
|
||||||
});
|
|
||||||
|
|
||||||
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findOne({
|
|
||||||
slug,
|
|
||||||
projectMembershipId: identityProjectMembership.id
|
|
||||||
});
|
|
||||||
if (!identityPrivilege) {
|
|
||||||
throw new NotFoundError({
|
|
||||||
message: `Identity additional privilege with slug '${slug}' not found for the specified identity with ID '${identityProjectMembership.identityId}'`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (data?.slug) {
|
|
||||||
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
|
|
||||||
slug: data.slug,
|
|
||||||
projectMembershipId: identityProjectMembership.id
|
|
||||||
});
|
|
||||||
if (existingSlug && existingSlug.id !== identityPrivilege.id)
|
|
||||||
throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const isTemporary = typeof data?.isTemporary !== "undefined" ? data.isTemporary : identityPrivilege.isTemporary;
|
|
||||||
validateHandlebarTemplate("Identity Additional Privilege Update", JSON.stringify(data.permissions || []), {
|
|
||||||
allowedExpressions: (val) => val.includes("identity.")
|
|
||||||
});
|
|
||||||
|
|
||||||
const packedPermission = data.permissions ? JSON.stringify(packRules(data.permissions)) : undefined;
|
|
||||||
if (isTemporary) {
|
|
||||||
const temporaryAccessStartTime = data?.temporaryAccessStartTime || identityPrivilege?.temporaryAccessStartTime;
|
|
||||||
const temporaryRange = data?.temporaryRange || identityPrivilege?.temporaryRange;
|
|
||||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
|
|
||||||
slug: data.slug,
|
|
||||||
permissions: packedPermission,
|
|
||||||
isTemporary: data.isTemporary,
|
|
||||||
temporaryRange: data.temporaryRange,
|
|
||||||
temporaryMode: data.temporaryMode,
|
|
||||||
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
|
|
||||||
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
|
|
||||||
});
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(identityProjectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
|
|
||||||
slug: data.slug,
|
|
||||||
permissions: packedPermission,
|
|
||||||
isTemporary: false,
|
|
||||||
temporaryAccessStartTime: null,
|
|
||||||
temporaryAccessEndTime: null,
|
|
||||||
temporaryRange: null,
|
|
||||||
temporaryMode: null
|
|
||||||
});
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(identityProjectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteBySlug = async ({
|
|
||||||
actorId,
|
|
||||||
slug,
|
|
||||||
identityId,
|
|
||||||
projectSlug,
|
|
||||||
actor,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod
|
|
||||||
}: TDeleteIdentityPrivilegeDTO) => {
|
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
|
||||||
const projectId = project.id;
|
|
||||||
|
|
||||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
|
||||||
if (!identityProjectMembership)
|
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
|
||||||
|
|
||||||
const { permission, membership } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
ProjectPermissionIdentityActions.Edit,
|
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
|
||||||
);
|
|
||||||
|
|
||||||
const { permission: identityRolePermission } = await permissionService.getProjectPermission({
|
|
||||||
actor: ActorType.IDENTITY,
|
|
||||||
actorId: identityProjectMembership.identityId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Identity,
|
|
||||||
permission,
|
|
||||||
identityRolePermission
|
|
||||||
);
|
|
||||||
if (!permissionBoundary.isValid)
|
|
||||||
throw new PermissionBoundaryError({
|
|
||||||
message: constructPermissionErrorMessage(
|
|
||||||
"Failed to edit more privileged identity",
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Identity
|
|
||||||
),
|
|
||||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
|
||||||
});
|
|
||||||
|
|
||||||
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findOne({
|
|
||||||
slug,
|
|
||||||
projectMembershipId: identityProjectMembership.id
|
|
||||||
});
|
|
||||||
if (!identityPrivilege) {
|
|
||||||
throw new NotFoundError({
|
|
||||||
message: `Identity additional privilege with slug '${slug}' not found for the specified identity with ID '${identityProjectMembership.identityId}'`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(identityProjectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...deletedPrivilege,
|
|
||||||
permissions: unpackPermissions(deletedPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPrivilegeDetailsBySlug = async ({
|
|
||||||
projectSlug,
|
|
||||||
identityId,
|
|
||||||
slug,
|
|
||||||
actorOrgId,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod
|
|
||||||
}: TGetIdentityPrivilegeDetailsDTO) => {
|
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
|
||||||
const projectId = project.id;
|
|
||||||
|
|
||||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
|
||||||
if (!identityProjectMembership)
|
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
ProjectPermissionIdentityActions.Read,
|
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
|
||||||
);
|
|
||||||
|
|
||||||
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findOne({
|
|
||||||
slug,
|
|
||||||
projectMembershipId: identityProjectMembership.id
|
|
||||||
});
|
|
||||||
if (!identityPrivilege) {
|
|
||||||
throw new NotFoundError({
|
|
||||||
message: `Identity additional privilege with slug '${slug}' not found for the specified identity with ID '${identityProjectMembership.identityId}'`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...identityPrivilege,
|
|
||||||
permissions: unpackPermissions(identityPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const listIdentityProjectPrivileges = async ({
|
|
||||||
identityId,
|
|
||||||
actorOrgId,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod,
|
|
||||||
projectSlug
|
|
||||||
}: TListIdentityPrivilegesDTO) => {
|
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
|
||||||
const projectId = project.id;
|
|
||||||
|
|
||||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
|
||||||
if (!identityProjectMembership)
|
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: identityProjectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
ProjectPermissionIdentityActions.Read,
|
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
|
||||||
);
|
|
||||||
|
|
||||||
const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find({
|
|
||||||
projectMembershipId: identityProjectMembership.id
|
|
||||||
});
|
|
||||||
return identityPrivileges.map((el) => ({
|
|
||||||
...el,
|
|
||||||
permissions: unpackPermissions(el.permissions)
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
create,
|
|
||||||
updateBySlug,
|
|
||||||
deleteBySlug,
|
|
||||||
getPrivilegeDetailsBySlug,
|
|
||||||
listIdentityProjectPrivileges
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
import { TProjectPermission } from "@app/lib/types";
|
|
||||||
|
|
||||||
import { TProjectPermissionV2Schema } from "../permission/project-permission";
|
|
||||||
|
|
||||||
export enum IdentityProjectAdditionalPrivilegeTemporaryMode {
|
|
||||||
Relative = "relative"
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TCreateIdentityPrivilegeDTO = {
|
|
||||||
permissions: TProjectPermissionV2Schema[];
|
|
||||||
identityId: string;
|
|
||||||
projectSlug: string;
|
|
||||||
slug: string;
|
|
||||||
} & (
|
|
||||||
| {
|
|
||||||
isTemporary: false;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
isTemporary: true;
|
|
||||||
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative;
|
|
||||||
temporaryRange: string;
|
|
||||||
temporaryAccessStartTime: string;
|
|
||||||
}
|
|
||||||
) &
|
|
||||||
Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TUpdateIdentityPrivilegeDTO = { slug: string; identityId: string; projectSlug: string } & Omit<
|
|
||||||
TProjectPermission,
|
|
||||||
"projectId"
|
|
||||||
> & {
|
|
||||||
data: Partial<{
|
|
||||||
permissions: TProjectPermissionV2Schema[];
|
|
||||||
slug: string;
|
|
||||||
isTemporary: boolean;
|
|
||||||
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative;
|
|
||||||
temporaryRange: string;
|
|
||||||
temporaryAccessStartTime: string;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TDeleteIdentityPrivilegeDTO = Omit<TProjectPermission, "projectId"> & {
|
|
||||||
slug: string;
|
|
||||||
identityId: string;
|
|
||||||
projectSlug: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TGetIdentityPrivilegeDetailsDTO = Omit<TProjectPermission, "projectId"> & {
|
|
||||||
slug: string;
|
|
||||||
identityId: string;
|
|
||||||
projectSlug: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TListIdentityPrivilegesDTO = Omit<TProjectPermission, "projectId"> & {
|
|
||||||
identityId: string;
|
|
||||||
projectSlug: string;
|
|
||||||
};
|
|
||||||
@@ -183,7 +183,8 @@ export const kmipOperationServiceFactory = ({
|
|||||||
algorithm: completeKeyDetails.internalKms.encryptionAlgorithm,
|
algorithm: completeKeyDetails.internalKms.encryptionAlgorithm,
|
||||||
isActive: !key.isDisabled,
|
isActive: !key.isDisabled,
|
||||||
createdAt: key.createdAt,
|
createdAt: key.createdAt,
|
||||||
updatedAt: key.updatedAt
|
updatedAt: key.updatedAt,
|
||||||
|
kmipMetadata: key.kmipMetadata as Record<string, unknown>
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -373,7 +374,8 @@ export const kmipOperationServiceFactory = ({
|
|||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
kmipMetadata
|
||||||
}: TKmipRegisterDTO) => {
|
}: TKmipRegisterDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
@@ -405,7 +407,8 @@ export const kmipOperationServiceFactory = ({
|
|||||||
isReserved: false,
|
isReserved: false,
|
||||||
projectId,
|
projectId,
|
||||||
keyUsage: KmsKeyUsage.ENCRYPT_DECRYPT,
|
keyUsage: KmsKeyUsage.ENCRYPT_DECRYPT,
|
||||||
orgId: project.orgId
|
orgId: project.orgId,
|
||||||
|
kmipMetadata
|
||||||
});
|
});
|
||||||
|
|
||||||
return kmsKey;
|
return kmsKey;
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ export type TKmipRegisterDTO = {
|
|||||||
name: string;
|
name: string;
|
||||||
key: string;
|
key: string;
|
||||||
algorithm: SymmetricKeyAlgorithm;
|
algorithm: SymmetricKeyAlgorithm;
|
||||||
|
kmipMetadata?: Record<string, unknown>;
|
||||||
} & KmipOperationBaseDTO;
|
} & KmipOperationBaseDTO;
|
||||||
|
|
||||||
export type TSetupOrgKmipDTO = {
|
export type TSetupOrgKmipDTO = {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { OrgMembershipStatus, TableName, TLdapConfigsUpdate, TUsers } from "@app/db/schemas";
|
import { AccessScope, OrgMembershipStatus, 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";
|
||||||
@@ -12,12 +12,12 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/
|
|||||||
import { AuthMethod, 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 { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||||
import { TokenType } from "@app/services/auth-token/auth-token-types";
|
import { TokenType } from "@app/services/auth-token/auth-token-types";
|
||||||
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
|
import { TMembershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
||||||
|
import { TMembershipGroupDALFactory } from "@app/services/membership-group/membership-group-dal";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { getDefaultOrgMembershipRole } from "@app/services/org/org-role-fns";
|
import { getDefaultOrgMembershipRole } from "@app/services/org/org-role-fns";
|
||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
||||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||||
@@ -49,13 +49,13 @@ import { TLdapGroupMapDALFactory } from "./ldap-group-map-dal";
|
|||||||
type TLdapConfigServiceFactoryDep = {
|
type TLdapConfigServiceFactoryDep = {
|
||||||
ldapConfigDAL: Pick<TLdapConfigDALFactory, "create" | "update" | "findOne" | "transaction">;
|
ldapConfigDAL: Pick<TLdapConfigDALFactory, "create" | "update" | "findOne" | "transaction">;
|
||||||
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"
|
||||||
>;
|
>;
|
||||||
groupDAL: Pick<TGroupDALFactory, "find" | "findOne">;
|
groupDAL: Pick<TGroupDALFactory, "find" | "findOne">;
|
||||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
membershipGroupDAL: Pick<TMembershipGroupDALFactory, "find">;
|
||||||
|
membershipRoleDAL: Pick<TMembershipRoleDALFactory, "create">;
|
||||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany" | "delete">;
|
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany" | "delete">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findById">;
|
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findById">;
|
||||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
||||||
@@ -87,9 +87,9 @@ export const ldapConfigServiceFactory = ({
|
|||||||
ldapConfigDAL,
|
ldapConfigDAL,
|
||||||
ldapGroupMapDAL,
|
ldapGroupMapDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
groupDAL,
|
groupDAL,
|
||||||
groupProjectDAL,
|
membershipGroupDAL,
|
||||||
|
membershipRoleDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
@@ -388,25 +388,33 @@ export const ldapConfigServiceFactory = ({
|
|||||||
await userDAL.transaction(async (tx) => {
|
await userDAL.transaction(async (tx) => {
|
||||||
const [orgMembership] = await orgDAL.findMembership(
|
const [orgMembership] = await orgDAL.findMembership(
|
||||||
{
|
{
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: userAlias.userId,
|
[`${TableName.Membership}.actorUserId` as "actorUserId"]: userAlias.userId,
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||||
|
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
);
|
);
|
||||||
if (!orgMembership) {
|
if (!orgMembership) {
|
||||||
const { role, roleId } = await getDefaultOrgMembershipRole(organization.defaultMembershipRole);
|
const { role, roleId } = await getDefaultOrgMembershipRole(organization.defaultMembershipRole);
|
||||||
|
|
||||||
await orgDAL.createMembership(
|
const membership = await orgDAL.createMembership(
|
||||||
{
|
{
|
||||||
userId: userAlias.userId,
|
actorUserId: userAlias.userId,
|
||||||
orgId,
|
scopeOrgId: orgId,
|
||||||
role,
|
scope: AccessScope.Organization,
|
||||||
roleId,
|
|
||||||
status: OrgMembershipStatus.Accepted,
|
status: OrgMembershipStatus.Accepted,
|
||||||
isActive: true
|
isActive: true
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
await membershipRoleDAL.create(
|
||||||
|
{
|
||||||
|
membershipId: membership.id,
|
||||||
|
role,
|
||||||
|
customRoleId: roleId
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
} else if (orgMembership.status === OrgMembershipStatus.Invited) {
|
} else if (orgMembership.status === OrgMembershipStatus.Invited) {
|
||||||
await orgDAL.updateMembershipById(
|
await orgDAL.updateMembershipById(
|
||||||
orgMembership.id,
|
orgMembership.id,
|
||||||
@@ -459,8 +467,9 @@ export const ldapConfigServiceFactory = ({
|
|||||||
|
|
||||||
const [orgMembership] = await orgDAL.findMembership(
|
const [orgMembership] = await orgDAL.findMembership(
|
||||||
{
|
{
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: newUser.id,
|
[`${TableName.Membership}.actorUserId` as "actorUserId"]: newUserAlias.userId,
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||||
|
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
);
|
);
|
||||||
@@ -469,16 +478,22 @@ export const ldapConfigServiceFactory = ({
|
|||||||
await throwOnPlanSeatLimitReached(licenseService, orgId, UserAliasType.LDAP);
|
await throwOnPlanSeatLimitReached(licenseService, orgId, UserAliasType.LDAP);
|
||||||
|
|
||||||
const { role, roleId } = await getDefaultOrgMembershipRole(organization.defaultMembershipRole);
|
const { role, roleId } = await getDefaultOrgMembershipRole(organization.defaultMembershipRole);
|
||||||
|
const membership = await orgDAL.createMembership(
|
||||||
await orgMembershipDAL.create(
|
|
||||||
{
|
{
|
||||||
userId: newUser.id,
|
actorUserId: newUser.id,
|
||||||
inviteEmail: email.toLowerCase(),
|
scopeOrgId: orgId,
|
||||||
orgId,
|
scope: AccessScope.Organization,
|
||||||
role,
|
|
||||||
roleId,
|
|
||||||
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
|
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
|
||||||
isActive: true
|
isActive: true,
|
||||||
|
inviteEmail: email.toLowerCase()
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
await membershipRoleDAL.create(
|
||||||
|
{
|
||||||
|
membershipId: membership.id,
|
||||||
|
role,
|
||||||
|
customRoleId: roleId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -542,10 +557,10 @@ export const ldapConfigServiceFactory = ({
|
|||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
groupProjectDAL,
|
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
|
membershipGroupDAL,
|
||||||
tx
|
tx
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -566,7 +581,7 @@ export const ldapConfigServiceFactory = ({
|
|||||||
userIds: [newUser.id],
|
userIds: [newUser.id],
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupProjectDAL,
|
membershipGroupDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
tx
|
tx
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { OrgMembershipStatus, TableName } from "@app/db/schemas";
|
import { AccessScope, OrgMembershipStatus, TableName } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
|
|
||||||
export type TLicenseDALFactory = ReturnType<typeof licenseDALFactory>;
|
export type TLicenseDALFactory = ReturnType<typeof licenseDALFactory>;
|
||||||
@@ -9,14 +9,14 @@ export type TLicenseDALFactory = ReturnType<typeof licenseDALFactory>;
|
|||||||
export const licenseDALFactory = (db: TDbClient) => {
|
export const licenseDALFactory = (db: TDbClient) => {
|
||||||
const countOfOrgMembers = async (orgId: string | null, tx?: Knex) => {
|
const countOfOrgMembers = async (orgId: string | null, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const doc = await (tx || db.replicaNode())(TableName.OrgMembership)
|
const doc = await (tx || db.replicaNode())(TableName.Membership)
|
||||||
.where({ status: OrgMembershipStatus.Accepted })
|
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
||||||
.andWhere((bd) => {
|
.andWhere((bd) => {
|
||||||
if (orgId) {
|
if (orgId) {
|
||||||
void bd.where({ orgId });
|
void bd.where(`${TableName.Membership}.scopeOrgId`, orgId);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||||
.where(`${TableName.Users}.isGhost`, false)
|
.where(`${TableName.Users}.isGhost`, false)
|
||||||
.count();
|
.count();
|
||||||
return Number(doc?.[0]?.count ?? 0);
|
return Number(doc?.[0]?.count ?? 0);
|
||||||
@@ -28,24 +28,27 @@ export const licenseDALFactory = (db: TDbClient) => {
|
|||||||
const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => {
|
const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
// count org users
|
// count org users
|
||||||
const userDoc = await (tx || db.replicaNode())(TableName.OrgMembership)
|
const userDoc = await (tx || db.replicaNode())(TableName.Membership)
|
||||||
.where({ status: OrgMembershipStatus.Accepted })
|
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
||||||
|
.whereNotNull(`${TableName.Membership}.actorUserId`)
|
||||||
.andWhere((bd) => {
|
.andWhere((bd) => {
|
||||||
if (orgId) {
|
if (orgId) {
|
||||||
void bd.where({ orgId });
|
void bd.where(`${TableName.Membership}.scopeOrgId`, orgId);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
.join(TableName.Users, `${TableName.Membership}.actorUserId`, `${TableName.Users}.id`)
|
||||||
.where(`${TableName.Users}.isGhost`, false)
|
.where(`${TableName.Users}.isGhost`, false)
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
const userCount = Number(userDoc?.[0].count);
|
const userCount = Number(userDoc?.[0].count);
|
||||||
|
|
||||||
// count org identities
|
// count org identities
|
||||||
const identityDoc = await (tx || db.replicaNode())(TableName.IdentityOrgMembership)
|
const identityDoc = await (tx || db.replicaNode())(TableName.Membership)
|
||||||
|
.where({ status: OrgMembershipStatus.Accepted, scope: AccessScope.Organization })
|
||||||
|
.whereNotNull(`${TableName.Membership}.actorIdentityId`)
|
||||||
.where((bd) => {
|
.where((bd) => {
|
||||||
if (orgId) {
|
if (orgId) {
|
||||||
void bd.where({ orgId });
|
void bd.where(`${TableName.Membership}.scopeOrgId`, orgId);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.count();
|
.count();
|
||||||
|
|||||||
@@ -488,7 +488,7 @@ export const licenseServiceFactory = ({
|
|||||||
const getUsageMetrics = async (orgId: string) => {
|
const getUsageMetrics = async (orgId: string) => {
|
||||||
const [orgMembersUsed, identityUsed, projectCount] = await Promise.all([
|
const [orgMembersUsed, identityUsed, projectCount] = await Promise.all([
|
||||||
orgDAL.countAllOrgMembers(orgId),
|
orgDAL.countAllOrgMembers(orgId),
|
||||||
identityOrgMembershipDAL.countAllOrgIdentities({ orgId }),
|
identityOrgMembershipDAL.countAllOrgIdentities({ scopeOrgId: orgId }),
|
||||||
projectDAL.countOfOrgProjects(orgId)
|
projectDAL.countOfOrgProjects(orgId)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { Issuer, Issuer as OpenIdIssuer, Strategy as OpenIdStrategy, TokenSet } from "openid-client";
|
import { Issuer, Issuer as OpenIdIssuer, Strategy as OpenIdStrategy, TokenSet } from "openid-client";
|
||||||
|
|
||||||
import { OrgMembershipStatus, TableName, TUsers } from "@app/db/schemas";
|
import { AccessScope, OrgMembershipStatus, TableName, TUsers } from "@app/db/schemas";
|
||||||
import { TOidcConfigsUpdate } from "@app/db/schemas/oidc-configs";
|
import { TOidcConfigsUpdate } from "@app/db/schemas/oidc-configs";
|
||||||
import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
||||||
@@ -19,12 +19,12 @@ import { OrgServiceActor } from "@app/lib/types";
|
|||||||
import { ActorType, AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||||
import { TokenType } from "@app/services/auth-token/auth-token-types";
|
import { TokenType } from "@app/services/auth-token/auth-token-types";
|
||||||
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
|
import { TMembershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
||||||
|
import { TMembershipGroupDALFactory } from "@app/services/membership-group/membership-group-dal";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { getDefaultOrgMembershipRole } from "@app/services/org/org-role-fns";
|
import { getDefaultOrgMembershipRole } from "@app/services/org/org-role-fns";
|
||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
||||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||||
@@ -62,11 +62,12 @@ type TOidcConfigServiceFactoryDep = {
|
|||||||
TOrgDALFactory,
|
TOrgDALFactory,
|
||||||
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
|
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
|
||||||
>;
|
>;
|
||||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">;
|
membershipGroupDAL: Pick<TMembershipGroupDALFactory, "find">;
|
||||||
|
membershipRoleDAL: Pick<TMembershipRoleDALFactory, "create">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
|
||||||
tokenService: Pick<TAuthTokenServiceFactory, "createTokenForUser">;
|
tokenService: Pick<TAuthTokenServiceFactory, "createTokenForUser">;
|
||||||
smtpService: Pick<TSmtpService, "sendMail" | "verify">;
|
smtpService: Pick<TSmtpService, "sendMail" | "verify">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getUserOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
oidcConfigDAL: Pick<TOidcConfigDALFactory, "findOne" | "update" | "create">;
|
oidcConfigDAL: Pick<TOidcConfigDALFactory, "findOne" | "update" | "create">;
|
||||||
groupDAL: Pick<TGroupDALFactory, "findByOrgId">;
|
groupDAL: Pick<TGroupDALFactory, "findByOrgId">;
|
||||||
userGroupMembershipDAL: Pick<
|
userGroupMembershipDAL: Pick<
|
||||||
@@ -78,7 +79,6 @@ type TOidcConfigServiceFactoryDep = {
|
|||||||
| "delete"
|
| "delete"
|
||||||
| "filterProjectsByUserMembership"
|
| "filterProjectsByUserMembership"
|
||||||
>;
|
>;
|
||||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
|
||||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany" | "delete">;
|
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany" | "delete">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findById">;
|
projectDAL: Pick<TProjectDALFactory, "findProjectGhostUser" | "findById">;
|
||||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
||||||
@@ -90,7 +90,6 @@ export type TOidcConfigServiceFactory = ReturnType<typeof oidcConfigServiceFacto
|
|||||||
|
|
||||||
export const oidcConfigServiceFactory = ({
|
export const oidcConfigServiceFactory = ({
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
userDAL,
|
userDAL,
|
||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
@@ -100,7 +99,8 @@ export const oidcConfigServiceFactory = ({
|
|||||||
oidcConfigDAL,
|
oidcConfigDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
groupProjectDAL,
|
membershipGroupDAL,
|
||||||
|
membershipRoleDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
@@ -196,26 +196,33 @@ export const oidcConfigServiceFactory = ({
|
|||||||
const foundUser = await userDAL.findById(userAlias.userId, tx);
|
const foundUser = await userDAL.findById(userAlias.userId, tx);
|
||||||
const [orgMembership] = await orgDAL.findMembership(
|
const [orgMembership] = await orgDAL.findMembership(
|
||||||
{
|
{
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: foundUser.id,
|
[`${TableName.Membership}.actorUserId` as "actorUserId"]: userAlias.userId,
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||||
|
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
);
|
);
|
||||||
if (!orgMembership) {
|
if (!orgMembership) {
|
||||||
const { role, roleId } = await getDefaultOrgMembershipRole(organization.defaultMembershipRole);
|
const { role, roleId } = await getDefaultOrgMembershipRole(organization.defaultMembershipRole);
|
||||||
|
|
||||||
await orgMembershipDAL.create(
|
const membership = await orgDAL.createMembership(
|
||||||
{
|
{
|
||||||
userId: userAlias.userId,
|
actorUserId: userAlias.userId,
|
||||||
inviteEmail: email,
|
scopeOrgId: orgId,
|
||||||
orgId,
|
scope: AccessScope.Organization,
|
||||||
role,
|
status: OrgMembershipStatus.Accepted,
|
||||||
roleId,
|
|
||||||
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
|
|
||||||
isActive: true
|
isActive: true
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
await membershipRoleDAL.create(
|
||||||
|
{
|
||||||
|
membershipId: membership.id,
|
||||||
|
role,
|
||||||
|
customRoleId: roleId
|
||||||
|
},
|
||||||
|
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 && foundUser.isAccepted) {
|
||||||
await orgDAL.updateMembershipById(
|
await orgDAL.updateMembershipById(
|
||||||
@@ -288,8 +295,9 @@ export const oidcConfigServiceFactory = ({
|
|||||||
|
|
||||||
const [orgMembership] = await orgDAL.findMembership(
|
const [orgMembership] = await orgDAL.findMembership(
|
||||||
{
|
{
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: newUser.id,
|
[`${TableName.Membership}.actorUserId` as "actorUserId"]: userAlias.userId,
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||||
|
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
);
|
);
|
||||||
@@ -299,15 +307,22 @@ export const oidcConfigServiceFactory = ({
|
|||||||
|
|
||||||
const { role, roleId } = await getDefaultOrgMembershipRole(organization.defaultMembershipRole);
|
const { role, roleId } = await getDefaultOrgMembershipRole(organization.defaultMembershipRole);
|
||||||
|
|
||||||
await orgMembershipDAL.create(
|
const membership = await orgDAL.createMembership(
|
||||||
{
|
{
|
||||||
userId: newUser.id,
|
actorUserId: newUser.id,
|
||||||
inviteEmail: email,
|
scopeOrgId: orgId,
|
||||||
orgId,
|
scope: AccessScope.Organization,
|
||||||
role,
|
|
||||||
roleId,
|
|
||||||
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
|
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
|
||||||
isActive: true
|
isActive: true,
|
||||||
|
inviteEmail: email.toLowerCase()
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
await membershipRoleDAL.create(
|
||||||
|
{
|
||||||
|
membershipId: membership.id,
|
||||||
|
role,
|
||||||
|
customRoleId: roleId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -341,7 +356,7 @@ export const oidcConfigServiceFactory = ({
|
|||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
groupProjectDAL,
|
membershipGroupDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL
|
projectBotDAL
|
||||||
@@ -378,7 +393,7 @@ export const oidcConfigServiceFactory = ({
|
|||||||
group,
|
group,
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupProjectDAL,
|
membershipGroupDAL,
|
||||||
projectKeyDAL
|
projectKeyDAL
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -749,7 +764,7 @@ export const oidcConfigServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isOidcManageGroupMembershipsEnabled = async (orgId: string, actor: OrgServiceActor) => {
|
const isOidcManageGroupMembershipsEnabled = async (orgId: string, actor: OrgServiceActor) => {
|
||||||
await permissionService.getUserOrgPermission(actor.id, orgId, actor.authMethod, actor.orgId);
|
await permissionService.getOrgPermission(ActorType.USER, actor.id, orgId, actor.authMethod, actor.orgId);
|
||||||
|
|
||||||
const oidcConfig = await oidcConfigDAL.findOne({
|
const oidcConfig = await oidcConfigDAL.findOne({
|
||||||
orgId,
|
orgId,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import knex, { Knex } from "knex";
|
import knex, { Knex } from "knex";
|
||||||
|
import tls, { PeerCertificate } from "tls";
|
||||||
|
|
||||||
import { verifyHostInputValidity } from "@app/ee/services/dynamic-secret/dynamic-secret-fns";
|
import { verifyHostInputValidity } from "@app/ee/services/dynamic-secret/dynamic-secret-fns";
|
||||||
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||||
@@ -30,7 +31,12 @@ const getConnectionConfig = (
|
|||||||
? {
|
? {
|
||||||
rejectUnauthorized: sslRejectUnauthorized,
|
rejectUnauthorized: sslRejectUnauthorized,
|
||||||
ca: sslCertificate,
|
ca: sslCertificate,
|
||||||
servername: host
|
servername: host,
|
||||||
|
// When using proxy, we need to bypass hostname validation since we connect to localhost
|
||||||
|
// but validate the certificate against the actual hostname
|
||||||
|
checkServerIdentity: (hostname: string, cert: PeerCertificate) => {
|
||||||
|
return tls.checkServerIdentity(host, cert);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
: false
|
: false
|
||||||
};
|
};
|
||||||
@@ -114,6 +120,10 @@ export const sqlResourceFactory: TPamResourceFactory<TSqlResourceConnectionDetai
|
|||||||
return connectionDetails;
|
return connectionDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (error.message.includes("no pg_hba.conf entry for host")) {
|
||||||
|
return connectionDetails;
|
||||||
|
}
|
||||||
|
|
||||||
if (error.message === "Connection terminated unexpectedly") {
|
if (error.message === "Connection terminated unexpectedly") {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Connection terminated unexpectedly. Verify that host and port are correct"
|
message: "Connection terminated unexpectedly. Verify that host and port are correct"
|
||||||
|
|||||||
@@ -219,7 +219,9 @@ export const OrgPermissionSchema = z.discriminatedUnion("subject", [
|
|||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
subject: z.literal(OrgPermissionSubjects.Groups).describe("The entity this permission pertains to."),
|
subject: z.literal(OrgPermissionSubjects.Groups).describe("The entity this permission pertains to."),
|
||||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionGroupActions).describe(
|
||||||
|
"Describe what action an entity can take."
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
subject: z.literal(OrgPermissionSubjects.SecretScanning).describe("The entity this permission pertains to."),
|
subject: z.literal(OrgPermissionSubjects.SecretScanning).describe("The entity this permission pertains to."),
|
||||||
@@ -227,11 +229,15 @@ export const OrgPermissionSchema = z.discriminatedUnion("subject", [
|
|||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
subject: z.literal(OrgPermissionSubjects.Billing).describe("The entity this permission pertains to."),
|
subject: z.literal(OrgPermissionSubjects.Billing).describe("The entity this permission pertains to."),
|
||||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionBillingActions).describe(
|
||||||
|
"Describe what action an entity can take."
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
subject: z.literal(OrgPermissionSubjects.Identity).describe("The entity this permission pertains to."),
|
subject: z.literal(OrgPermissionSubjects.Identity).describe("The entity this permission pertains to."),
|
||||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionIdentityActions).describe(
|
||||||
|
"Describe what action an entity can take."
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
subject: z.literal(OrgPermissionSubjects.Kms).describe("The entity this permission pertains to."),
|
subject: z.literal(OrgPermissionSubjects.Kms).describe("The entity this permission pertains to."),
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
|||||||
import { ForbiddenError, MongoAbility, PureAbility, subject } from "@casl/ability";
|
import { ForbiddenError, MongoAbility, PureAbility, subject } from "@casl/ability";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { OrgMembershipRole, TOrganizations } from "@app/db/schemas";
|
import { TOrganizations } from "@app/db/schemas";
|
||||||
import { validatePermissionBoundary } from "@app/lib/casl/boundary";
|
import { validatePermissionBoundary } from "@app/lib/casl/boundary";
|
||||||
import { BadRequestError, ForbiddenRequestError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { ActorAuthMethod, AuthMethod } from "@app/services/auth/auth-type";
|
import { ActorAuthMethod, AuthMethod } from "@app/services/auth/auth-type";
|
||||||
@@ -123,13 +123,13 @@ function validateOrgSSO(
|
|||||||
isOrgSsoEnforced: TOrganizations["authEnforced"],
|
isOrgSsoEnforced: TOrganizations["authEnforced"],
|
||||||
isOrgGoogleSsoEnforced: TOrganizations["googleSsoAuthEnforced"],
|
isOrgGoogleSsoEnforced: TOrganizations["googleSsoAuthEnforced"],
|
||||||
isOrgSsoBypassEnabled: TOrganizations["bypassOrgAuthEnabled"],
|
isOrgSsoBypassEnabled: TOrganizations["bypassOrgAuthEnabled"],
|
||||||
orgRole: OrgMembershipRole
|
isAdmin: boolean
|
||||||
) {
|
) {
|
||||||
if (actorAuthMethod === undefined) {
|
if (actorAuthMethod === undefined) {
|
||||||
throw new UnauthorizedError({ name: "No auth method defined" });
|
throw new UnauthorizedError({ name: "No auth method defined" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((isOrgSsoEnforced || isOrgGoogleSsoEnforced) && isOrgSsoBypassEnabled && orgRole === OrgMembershipRole.Admin) {
|
if ((isOrgSsoEnforced || isOrgGoogleSsoEnforced) && isOrgSsoBypassEnabled && isAdmin) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { MongoAbility, RawRuleOf } from "@casl/ability";
|
import { MongoAbility } from "@casl/ability";
|
||||||
import { MongoQuery } from "@ucast/mongo2js";
|
import { MongoQuery } from "@ucast/mongo2js";
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType, TMemberships } from "@app/db/schemas";
|
||||||
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
import { OrgPermissionSet } from "./org-permission";
|
import { OrgPermissionSet } from "./org-permission";
|
||||||
@@ -49,232 +49,90 @@ export type TGetProjectPermissionArg = {
|
|||||||
actionProjectType: ActionProjectType;
|
actionProjectType: ActionProjectType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TGetOrgPermissionArg = {
|
||||||
|
actor: ActorType;
|
||||||
|
actorId: string;
|
||||||
|
orgId: string;
|
||||||
|
actorAuthMethod: ActorAuthMethod;
|
||||||
|
actorOrgId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type TPermissionServiceFactory = {
|
export type TPermissionServiceFactory = {
|
||||||
getUserOrgPermission: (
|
|
||||||
userId: string,
|
|
||||||
orgId: string,
|
|
||||||
authMethod: ActorAuthMethod,
|
|
||||||
userOrgId?: string
|
|
||||||
) => Promise<{
|
|
||||||
permission: MongoAbility<OrgPermissionSet, MongoQuery>;
|
|
||||||
membership: {
|
|
||||||
status: string;
|
|
||||||
orgId: string;
|
|
||||||
id: string;
|
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
role: string;
|
|
||||||
isActive: boolean;
|
|
||||||
shouldUseNewPrivilegeSystem: boolean;
|
|
||||||
bypassOrgAuthEnabled: boolean;
|
|
||||||
permissions?: unknown;
|
|
||||||
userId?: string | null | undefined;
|
|
||||||
roleId?: string | null | undefined;
|
|
||||||
inviteEmail?: string | null | undefined;
|
|
||||||
projectFavorites?: string[] | null | undefined;
|
|
||||||
customRoleSlug?: string | null | undefined;
|
|
||||||
orgAuthEnforced?: boolean | null | undefined;
|
|
||||||
} & {
|
|
||||||
groups: {
|
|
||||||
id: string;
|
|
||||||
updatedAt: Date;
|
|
||||||
createdAt: Date;
|
|
||||||
role: string;
|
|
||||||
roleId: string | null | undefined;
|
|
||||||
customRolePermission: unknown;
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
orgId: string;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
}>;
|
|
||||||
getOrgPermission: (
|
getOrgPermission: (
|
||||||
type: ActorType,
|
type: ActorType,
|
||||||
id: string,
|
id: string,
|
||||||
orgId: string,
|
orgId: string,
|
||||||
authMethod: ActorAuthMethod,
|
authMethod: ActorAuthMethod,
|
||||||
actorOrgId: string | undefined
|
actorOrgId: string | undefined
|
||||||
) => Promise<
|
) => Promise<{
|
||||||
| {
|
permission: MongoAbility<OrgPermissionSet, MongoQuery>;
|
||||||
permission: MongoAbility<OrgPermissionSet, MongoQuery>;
|
memberships: Array<
|
||||||
membership: {
|
TMemberships & {
|
||||||
status: string;
|
roles: { role: string; customRoleSlug?: string | null }[];
|
||||||
orgId: string;
|
shouldUseNewPrivilegeSystem?: boolean | null;
|
||||||
id: string;
|
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
role: string;
|
|
||||||
isActive: boolean;
|
|
||||||
shouldUseNewPrivilegeSystem: boolean;
|
|
||||||
bypassOrgAuthEnabled: boolean;
|
|
||||||
permissions?: unknown;
|
|
||||||
userId?: string | null | undefined;
|
|
||||||
roleId?: string | null | undefined;
|
|
||||||
inviteEmail?: string | null | undefined;
|
|
||||||
projectFavorites?: string[] | null | undefined;
|
|
||||||
customRoleSlug?: string | null | undefined;
|
|
||||||
orgAuthEnforced?: boolean | null | undefined;
|
|
||||||
} & {
|
|
||||||
groups: {
|
|
||||||
id: string;
|
|
||||||
updatedAt: Date;
|
|
||||||
createdAt: Date;
|
|
||||||
role: string;
|
|
||||||
roleId: string | null | undefined;
|
|
||||||
customRolePermission: unknown;
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
orgId: string;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
| {
|
>;
|
||||||
permission: MongoAbility<OrgPermissionSet, MongoQuery>;
|
|
||||||
membership: {
|
|
||||||
id: string;
|
|
||||||
role: string;
|
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
orgId: string;
|
|
||||||
roleId?: string | null | undefined;
|
|
||||||
permissions?: unknown;
|
|
||||||
identityId: string;
|
|
||||||
orgAuthEnforced: boolean | null | undefined;
|
|
||||||
shouldUseNewPrivilegeSystem: boolean;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
getUserProjectPermission: ({
|
|
||||||
userId,
|
|
||||||
projectId,
|
|
||||||
authMethod,
|
|
||||||
userOrgId,
|
|
||||||
actionProjectType
|
|
||||||
}: TGetUserProjectPermissionArg) => Promise<{
|
|
||||||
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
|
||||||
membership: {
|
|
||||||
id: string;
|
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
userId: string;
|
|
||||||
projectId: string;
|
|
||||||
} & {
|
|
||||||
orgAuthEnforced: boolean | null | undefined;
|
|
||||||
orgId: string;
|
|
||||||
roles: Array<{
|
|
||||||
role: string;
|
|
||||||
}>;
|
|
||||||
shouldUseNewPrivilegeSystem: boolean;
|
|
||||||
};
|
|
||||||
hasRole: (role: string) => boolean;
|
hasRole: (role: string) => boolean;
|
||||||
}>;
|
}>;
|
||||||
getProjectPermission: <T extends ActorType>(
|
getProjectPermission: (arg: TGetProjectPermissionArg) => Promise<{
|
||||||
arg: TGetProjectPermissionArg
|
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
||||||
) => Promise<
|
memberships: Array<TMemberships & { roles: { role: string; customRoleSlug?: string | null }[] }>;
|
||||||
T extends ActorType.SERVICE
|
hasRole: (role: string) => boolean;
|
||||||
? {
|
}>;
|
||||||
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
getProjectPermissions: (
|
||||||
membership: {
|
projectId: string,
|
||||||
shouldUseNewPrivilegeSystem: boolean;
|
orgId: string
|
||||||
};
|
) => Promise<{
|
||||||
hasRole: (arg: string) => boolean;
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
|
||||||
membership: (T extends ActorType.USER
|
|
||||||
? {
|
|
||||||
id: string;
|
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
userId: string;
|
|
||||||
projectId: string;
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
id: string;
|
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
projectId: string;
|
|
||||||
identityId: string;
|
|
||||||
}) & {
|
|
||||||
orgAuthEnforced: boolean | null | undefined;
|
|
||||||
orgId: string;
|
|
||||||
roles: Array<{
|
|
||||||
role: string;
|
|
||||||
}>;
|
|
||||||
shouldUseNewPrivilegeSystem: boolean;
|
|
||||||
};
|
|
||||||
hasRole: (role: string) => boolean;
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
getProjectPermissions: (projectId: string) => Promise<{
|
|
||||||
userPermissions: {
|
userPermissions: {
|
||||||
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
membershipId: string;
|
|
||||||
}[];
|
}[];
|
||||||
identityPermissions: {
|
identityPermissions: {
|
||||||
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
membershipId: string;
|
|
||||||
}[];
|
}[];
|
||||||
groupPermissions: {
|
groupPermissions: {
|
||||||
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
membershipId: string;
|
|
||||||
}[];
|
}[];
|
||||||
}>;
|
}>;
|
||||||
getOrgPermissionByRole: (
|
getOrgPermissionByRoles: (
|
||||||
role: string,
|
roles: string[],
|
||||||
orgId: string
|
orgId: string
|
||||||
) => Promise<
|
) => Promise<
|
||||||
| {
|
{
|
||||||
permission: MongoAbility<OrgPermissionSet, MongoQuery>;
|
permission: MongoAbility<OrgPermissionSet, MongoQuery>;
|
||||||
role: {
|
role?: {
|
||||||
name: string;
|
name: string;
|
||||||
orgId: string;
|
id: string;
|
||||||
id: string;
|
createdAt: Date;
|
||||||
createdAt: Date;
|
updatedAt: Date;
|
||||||
updatedAt: Date;
|
slug: string;
|
||||||
slug: string;
|
permissions?: unknown;
|
||||||
permissions?: unknown;
|
description?: string | null | undefined;
|
||||||
description?: string | null | undefined;
|
};
|
||||||
};
|
}[]
|
||||||
}
|
|
||||||
| {
|
|
||||||
permission: MongoAbility<OrgPermissionSet, MongoQuery>;
|
|
||||||
role?: undefined;
|
|
||||||
}
|
|
||||||
>;
|
>;
|
||||||
getProjectPermissionByRole: (
|
getProjectPermissionByRoles: (
|
||||||
role: string,
|
roles: string[],
|
||||||
projectId: string
|
projectId: string
|
||||||
) => Promise<
|
) => Promise<
|
||||||
| {
|
{
|
||||||
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
||||||
role: {
|
role?: {
|
||||||
name: string;
|
name: string;
|
||||||
version: number;
|
id: string;
|
||||||
id: string;
|
createdAt: Date;
|
||||||
createdAt: Date;
|
updatedAt: Date;
|
||||||
updatedAt: Date;
|
slug: string;
|
||||||
projectId: string;
|
permissions?: unknown;
|
||||||
slug: string;
|
description?: string | null | undefined;
|
||||||
permissions?: unknown;
|
};
|
||||||
description?: string | null | undefined;
|
}[]
|
||||||
};
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
|
||||||
role?: undefined;
|
|
||||||
}
|
|
||||||
>;
|
>;
|
||||||
buildOrgPermission: (orgUserRoles: TBuildOrgPermissionDTO) => MongoAbility<OrgPermissionSet, MongoQuery>;
|
|
||||||
buildProjectPermissionRules: (
|
|
||||||
projectUserRoles: TBuildProjectPermissionDTO
|
|
||||||
) => RawRuleOf<MongoAbility<ProjectPermissionSet>>[];
|
|
||||||
checkGroupProjectPermission: ({
|
checkGroupProjectPermission: ({
|
||||||
groupId,
|
groupId,
|
||||||
projectId,
|
projectId,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
|||||||
import { AbilityBuilder, createMongoAbility, ForcedSubject, MongoAbility } from "@casl/ability";
|
import { AbilityBuilder, createMongoAbility, ForcedSubject, MongoAbility } from "@casl/ability";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { ProjectMembershipRole } from "@app/db/schemas";
|
||||||
import {
|
import {
|
||||||
CASL_ACTION_SCHEMA_ENUM,
|
CASL_ACTION_SCHEMA_ENUM,
|
||||||
CASL_ACTION_SCHEMA_NATIVE_ENUM
|
CASL_ACTION_SCHEMA_NATIVE_ENUM
|
||||||
@@ -199,6 +200,9 @@ export enum ProjectPermissionPamSessionActions {
|
|||||||
// Terminate = "terminate"
|
// Terminate = "terminate"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isCustomProjectRole = (slug: string) =>
|
||||||
|
!Object.values(ProjectMembershipRole).includes(slug as ProjectMembershipRole);
|
||||||
|
|
||||||
export enum ProjectPermissionSub {
|
export enum ProjectPermissionSub {
|
||||||
Role = "role",
|
Role = "role",
|
||||||
Member = "member",
|
Member = "member",
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import { TDbClient } from "@app/db";
|
|
||||||
import { TableName } from "@app/db/schemas";
|
|
||||||
import { ormify, TOrmify } from "@app/lib/knex";
|
|
||||||
|
|
||||||
export type TProjectUserAdditionalPrivilegeDALFactory = TOrmify<TableName.ProjectUserAdditionalPrivilege>;
|
|
||||||
|
|
||||||
export const projectUserAdditionalPrivilegeDALFactory = (db: TDbClient): TProjectUserAdditionalPrivilegeDALFactory => {
|
|
||||||
const orm = ormify(db, TableName.ProjectUserAdditionalPrivilege);
|
|
||||||
return orm;
|
|
||||||
};
|
|
||||||
@@ -1,388 +0,0 @@
|
|||||||
import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
|
|
||||||
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
|
||||||
|
|
||||||
import { ActionProjectType, TableName } from "@app/db/schemas";
|
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
|
||||||
import { ms } from "@app/lib/ms";
|
|
||||||
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
|
||||||
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
|
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
|
||||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
|
||||||
|
|
||||||
import { TAccessApprovalRequestDALFactory } from "../access-approval-request/access-approval-request-dal";
|
|
||||||
import { constructPermissionErrorMessage, validatePrivilegeChangeOperation } from "../permission/permission-fns";
|
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service-types";
|
|
||||||
import {
|
|
||||||
ProjectPermissionMemberActions,
|
|
||||||
ProjectPermissionSet,
|
|
||||||
ProjectPermissionSub
|
|
||||||
} from "../permission/project-permission";
|
|
||||||
import { ApprovalStatus } from "../secret-approval-request/secret-approval-request-types";
|
|
||||||
import { TProjectUserAdditionalPrivilegeDALFactory } from "./project-user-additional-privilege-dal";
|
|
||||||
import {
|
|
||||||
ProjectUserAdditionalPrivilegeTemporaryMode,
|
|
||||||
TProjectUserAdditionalPrivilegeServiceFactory
|
|
||||||
} from "./project-user-additional-privilege-types";
|
|
||||||
|
|
||||||
type TProjectUserAdditionalPrivilegeServiceFactoryDep = {
|
|
||||||
projectUserAdditionalPrivilegeDAL: TProjectUserAdditionalPrivilegeDALFactory;
|
|
||||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findById" | "findOne">;
|
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "invalidateProjectPermissionCache">;
|
|
||||||
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "update">;
|
|
||||||
};
|
|
||||||
|
|
||||||
const unpackPermissions = (permissions: unknown) =>
|
|
||||||
UnpackedPermissionSchema.array().parse(
|
|
||||||
unpackRules((permissions || []) as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[])
|
|
||||||
);
|
|
||||||
|
|
||||||
export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|
||||||
projectUserAdditionalPrivilegeDAL,
|
|
||||||
projectMembershipDAL,
|
|
||||||
permissionService,
|
|
||||||
accessApprovalRequestDAL
|
|
||||||
}: TProjectUserAdditionalPrivilegeServiceFactoryDep): TProjectUserAdditionalPrivilegeServiceFactory => {
|
|
||||||
const create: TProjectUserAdditionalPrivilegeServiceFactory["create"] = async ({
|
|
||||||
slug,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
permissions: customPermission,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod,
|
|
||||||
projectMembershipId,
|
|
||||||
...dto
|
|
||||||
}) => {
|
|
||||||
const projectMembership = await projectMembershipDAL.findById(projectMembershipId);
|
|
||||||
if (!projectMembership)
|
|
||||||
throw new NotFoundError({ message: `Project membership with ID ${projectMembershipId} found` });
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: projectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member);
|
|
||||||
const { permission: targetUserPermission, membership } = await permissionService.getProjectPermission({
|
|
||||||
actor: ActorType.USER,
|
|
||||||
actorId: projectMembership.userId,
|
|
||||||
projectId: projectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
|
|
||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
|
||||||
targetUserPermission.update(targetUserPermission.rules.concat(customPermission));
|
|
||||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionMemberActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Member,
|
|
||||||
permission,
|
|
||||||
targetUserPermission
|
|
||||||
);
|
|
||||||
if (!permissionBoundary.isValid)
|
|
||||||
throw new PermissionBoundaryError({
|
|
||||||
message: constructPermissionErrorMessage(
|
|
||||||
"Failed to update more privileged user",
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionMemberActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Member
|
|
||||||
),
|
|
||||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
|
||||||
});
|
|
||||||
|
|
||||||
const existingSlug = await projectUserAdditionalPrivilegeDAL.findOne({
|
|
||||||
slug,
|
|
||||||
projectId: projectMembership.projectId,
|
|
||||||
userId: projectMembership.userId
|
|
||||||
});
|
|
||||||
if (existingSlug)
|
|
||||||
throw new BadRequestError({ message: `Additional privilege with provided slug ${slug} already exists` });
|
|
||||||
|
|
||||||
validateHandlebarTemplate("User Additional Privilege Create", JSON.stringify(customPermission || []), {
|
|
||||||
allowedExpressions: (val) => val.includes("identity.")
|
|
||||||
});
|
|
||||||
|
|
||||||
const packedPermission = JSON.stringify(packRules(customPermission));
|
|
||||||
if (!dto.isTemporary) {
|
|
||||||
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.create({
|
|
||||||
userId: projectMembership.userId,
|
|
||||||
projectId: projectMembership.projectId,
|
|
||||||
slug,
|
|
||||||
permissions: packedPermission
|
|
||||||
});
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(projectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
|
|
||||||
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.create({
|
|
||||||
projectId: projectMembership.projectId,
|
|
||||||
userId: projectMembership.userId,
|
|
||||||
slug,
|
|
||||||
permissions: packedPermission,
|
|
||||||
isTemporary: true,
|
|
||||||
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
|
|
||||||
temporaryRange: dto.temporaryRange,
|
|
||||||
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
|
|
||||||
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
|
|
||||||
});
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(projectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateById: TProjectUserAdditionalPrivilegeServiceFactory["updateById"] = async ({
|
|
||||||
privilegeId,
|
|
||||||
actorOrgId,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod,
|
|
||||||
...dto
|
|
||||||
}) => {
|
|
||||||
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
|
||||||
if (!userPrivilege)
|
|
||||||
throw new NotFoundError({ message: `User additional privilege with ID ${privilegeId} not found` });
|
|
||||||
|
|
||||||
const projectMembership = await projectMembershipDAL.findOne({
|
|
||||||
userId: userPrivilege.userId,
|
|
||||||
projectId: userPrivilege.projectId
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!projectMembership)
|
|
||||||
throw new NotFoundError({
|
|
||||||
message: `Project membership for user with ID '${userPrivilege.userId}' not found in project with ID '${userPrivilege.projectId}'`
|
|
||||||
});
|
|
||||||
|
|
||||||
const { permission, membership } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: projectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member);
|
|
||||||
const { permission: targetUserPermission } = await permissionService.getProjectPermission({
|
|
||||||
actor: ActorType.USER,
|
|
||||||
actorId: projectMembership.userId,
|
|
||||||
projectId: projectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
|
|
||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
|
||||||
targetUserPermission.update(targetUserPermission.rules.concat(dto.permissions || []));
|
|
||||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionMemberActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Member,
|
|
||||||
permission,
|
|
||||||
targetUserPermission
|
|
||||||
);
|
|
||||||
if (!permissionBoundary.isValid)
|
|
||||||
throw new PermissionBoundaryError({
|
|
||||||
message: constructPermissionErrorMessage(
|
|
||||||
"Failed to update more privileged user",
|
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
|
||||||
ProjectPermissionMemberActions.GrantPrivileges,
|
|
||||||
ProjectPermissionSub.Member
|
|
||||||
),
|
|
||||||
details: { missingPermissions: permissionBoundary.missingPermissions }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (dto?.slug) {
|
|
||||||
const existingSlug = await projectUserAdditionalPrivilegeDAL.findOne({
|
|
||||||
slug: dto.slug,
|
|
||||||
userId: projectMembership.id,
|
|
||||||
projectId: projectMembership.projectId
|
|
||||||
});
|
|
||||||
if (existingSlug && existingSlug.id !== userPrivilege.id)
|
|
||||||
throw new BadRequestError({ message: `Additional privilege with provided slug ${dto.slug} already exists` });
|
|
||||||
}
|
|
||||||
|
|
||||||
validateHandlebarTemplate("User Additional Privilege Update", JSON.stringify(dto.permissions || []), {
|
|
||||||
allowedExpressions: (val) => val.includes("identity.")
|
|
||||||
});
|
|
||||||
|
|
||||||
const isTemporary = typeof dto?.isTemporary !== "undefined" ? dto.isTemporary : userPrivilege.isTemporary;
|
|
||||||
|
|
||||||
const packedPermission = dto.permissions && JSON.stringify(packRules(dto.permissions));
|
|
||||||
if (isTemporary) {
|
|
||||||
const temporaryAccessStartTime = dto?.temporaryAccessStartTime || userPrivilege?.temporaryAccessStartTime;
|
|
||||||
const temporaryRange = dto?.temporaryRange || userPrivilege?.temporaryRange;
|
|
||||||
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.updateById(userPrivilege.id, {
|
|
||||||
slug: dto.slug,
|
|
||||||
permissions: packedPermission,
|
|
||||||
isTemporary: dto.isTemporary,
|
|
||||||
temporaryRange: dto.temporaryRange,
|
|
||||||
temporaryMode: dto.temporaryMode,
|
|
||||||
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
|
|
||||||
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
|
|
||||||
});
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(projectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.updateById(userPrivilege.id, {
|
|
||||||
slug: dto.slug,
|
|
||||||
permissions: packedPermission,
|
|
||||||
isTemporary: false,
|
|
||||||
temporaryAccessStartTime: null,
|
|
||||||
temporaryAccessEndTime: null,
|
|
||||||
temporaryRange: null,
|
|
||||||
temporaryMode: null
|
|
||||||
});
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(projectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...additionalPrivilege,
|
|
||||||
permissions: unpackPermissions(additionalPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteById: TProjectUserAdditionalPrivilegeServiceFactory["deleteById"] = async ({
|
|
||||||
actorId,
|
|
||||||
actor,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod,
|
|
||||||
privilegeId
|
|
||||||
}) => {
|
|
||||||
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
|
||||||
if (!userPrivilege)
|
|
||||||
throw new NotFoundError({ message: `User additional privilege with ID ${privilegeId} not found` });
|
|
||||||
|
|
||||||
const projectMembership = await projectMembershipDAL.findOne({
|
|
||||||
userId: userPrivilege.userId,
|
|
||||||
projectId: userPrivilege.projectId
|
|
||||||
});
|
|
||||||
if (!projectMembership)
|
|
||||||
throw new NotFoundError({
|
|
||||||
message: `Project membership for user with ID '${userPrivilege.userId}' not found in project with ID '${userPrivilege.projectId}'`
|
|
||||||
});
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: projectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member);
|
|
||||||
|
|
||||||
await accessApprovalRequestDAL.update(
|
|
||||||
{
|
|
||||||
privilegeId: userPrivilege.id
|
|
||||||
},
|
|
||||||
{
|
|
||||||
privilegeDeletedAt: new Date(),
|
|
||||||
status: ApprovalStatus.REJECTED
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const deletedPrivilege = await projectUserAdditionalPrivilegeDAL.deleteById(userPrivilege.id);
|
|
||||||
|
|
||||||
await permissionService.invalidateProjectPermissionCache(projectMembership.projectId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...deletedPrivilege,
|
|
||||||
permissions: unpackPermissions(deletedPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPrivilegeDetailsById: TProjectUserAdditionalPrivilegeServiceFactory["getPrivilegeDetailsById"] = async ({
|
|
||||||
privilegeId,
|
|
||||||
actorOrgId,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod
|
|
||||||
}) => {
|
|
||||||
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
|
|
||||||
if (!userPrivilege)
|
|
||||||
throw new NotFoundError({ message: `User additional privilege with ID ${privilegeId} not found` });
|
|
||||||
|
|
||||||
const projectMembership = await projectMembershipDAL.findOne({
|
|
||||||
userId: userPrivilege.userId,
|
|
||||||
projectId: userPrivilege.projectId
|
|
||||||
});
|
|
||||||
if (!projectMembership)
|
|
||||||
throw new NotFoundError({
|
|
||||||
message: `Project membership for user with ID '${userPrivilege.userId}' not found in project with ID '${userPrivilege.projectId}'`
|
|
||||||
});
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: projectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...userPrivilege,
|
|
||||||
permissions: unpackPermissions(userPrivilege.permissions)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const listPrivileges: TProjectUserAdditionalPrivilegeServiceFactory["listPrivileges"] = async ({
|
|
||||||
projectMembershipId,
|
|
||||||
actorOrgId,
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod
|
|
||||||
}) => {
|
|
||||||
const projectMembership = await projectMembershipDAL.findById(projectMembershipId);
|
|
||||||
if (!projectMembership)
|
|
||||||
throw new NotFoundError({ message: `Project membership with ID ${projectMembershipId} not found` });
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId: projectMembership.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member);
|
|
||||||
|
|
||||||
const userPrivileges = await projectUserAdditionalPrivilegeDAL.find(
|
|
||||||
{
|
|
||||||
userId: projectMembership.userId,
|
|
||||||
projectId: projectMembership.projectId
|
|
||||||
},
|
|
||||||
{ sort: [[`${TableName.ProjectUserAdditionalPrivilege}.slug` as "slug", "asc"]] }
|
|
||||||
);
|
|
||||||
return userPrivileges;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
create,
|
|
||||||
updateById,
|
|
||||||
deleteById,
|
|
||||||
getPrivilegeDetailsById,
|
|
||||||
listPrivileges
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
import { TProjectUserAdditionalPrivilege } from "@app/db/schemas";
|
|
||||||
import { TProjectPermission } from "@app/lib/types";
|
|
||||||
|
|
||||||
import { TProjectPermissionV2Schema } from "../permission/project-permission";
|
|
||||||
|
|
||||||
export enum ProjectUserAdditionalPrivilegeTemporaryMode {
|
|
||||||
Relative = "relative"
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TCreateUserPrivilegeDTO = (
|
|
||||||
| {
|
|
||||||
permissions: TProjectPermissionV2Schema[];
|
|
||||||
projectMembershipId: string;
|
|
||||||
slug: string;
|
|
||||||
isTemporary: false;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
permissions: TProjectPermissionV2Schema[];
|
|
||||||
projectMembershipId: string;
|
|
||||||
slug: string;
|
|
||||||
isTemporary: true;
|
|
||||||
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative;
|
|
||||||
temporaryRange: string;
|
|
||||||
temporaryAccessStartTime: string;
|
|
||||||
}
|
|
||||||
) &
|
|
||||||
Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TUpdateUserPrivilegeDTO = { privilegeId: string } & Omit<TProjectPermission, "projectId"> &
|
|
||||||
Partial<{
|
|
||||||
permissions: TProjectPermissionV2Schema[];
|
|
||||||
slug: string;
|
|
||||||
isTemporary: boolean;
|
|
||||||
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative;
|
|
||||||
temporaryRange: string;
|
|
||||||
temporaryAccessStartTime: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export type TDeleteUserPrivilegeDTO = Omit<TProjectPermission, "projectId"> & { privilegeId: string };
|
|
||||||
|
|
||||||
export type TGetUserPrivilegeDetailsDTO = Omit<TProjectPermission, "projectId"> & { privilegeId: string };
|
|
||||||
|
|
||||||
export type TListUserPrivilegesDTO = Omit<TProjectPermission, "projectId"> & { projectMembershipId: string };
|
|
||||||
|
|
||||||
interface TAdditionalPrivilege extends TProjectUserAdditionalPrivilege {
|
|
||||||
permissions: {
|
|
||||||
action: string[];
|
|
||||||
subject?: string | undefined;
|
|
||||||
conditions?: unknown;
|
|
||||||
inverted?: boolean | undefined;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TProjectUserAdditionalPrivilegeServiceFactory = {
|
|
||||||
create: (arg: TCreateUserPrivilegeDTO) => Promise<TAdditionalPrivilege>;
|
|
||||||
updateById: (arg: TUpdateUserPrivilegeDTO) => Promise<TAdditionalPrivilege>;
|
|
||||||
deleteById: (arg: TDeleteUserPrivilegeDTO) => Promise<TAdditionalPrivilege>;
|
|
||||||
getPrivilegeDetailsById: (arg: TGetUserPrivilegeDetailsDTO) => Promise<TAdditionalPrivilege>;
|
|
||||||
listPrivileges: (arg: TListUserPrivilegesDTO) => Promise<TProjectUserAdditionalPrivilege[]>;
|
|
||||||
};
|
|
||||||
@@ -1,11 +1,47 @@
|
|||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName } from "@app/db/schemas";
|
import { TableName, TRelays } from "@app/db/schemas";
|
||||||
import { ormify } from "@app/lib/knex";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
|
import { buildFindFilter, ormify, TFindFilter, TFindOpt } from "@app/lib/knex";
|
||||||
|
|
||||||
export type TRelayDALFactory = ReturnType<typeof relayDalFactory>;
|
export type TRelayDALFactory = ReturnType<typeof relayDalFactory>;
|
||||||
|
|
||||||
export const relayDalFactory = (db: TDbClient) => {
|
export const relayDalFactory = (db: TDbClient) => {
|
||||||
const orm = ormify(db, TableName.Relay);
|
const orm = ormify(db, TableName.Relay);
|
||||||
|
|
||||||
return orm;
|
const find = async (
|
||||||
|
filter: TFindFilter<TRelays> & { isHeartbeatStale?: boolean },
|
||||||
|
{ offset, limit, sort, tx }: TFindOpt<TRelays> = {}
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const { isHeartbeatStale, ...regularFilter } = filter;
|
||||||
|
|
||||||
|
const query = (tx || db.replicaNode())(TableName.Relay)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
.where(buildFindFilter(regularFilter, TableName.Relay));
|
||||||
|
|
||||||
|
if (isHeartbeatStale) {
|
||||||
|
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
|
||||||
|
void query.whereNotNull(`${TableName.Relay}.heartbeat`);
|
||||||
|
void query.where(`${TableName.Relay}.heartbeat`, "<", oneHourAgo);
|
||||||
|
void query.where((v) => {
|
||||||
|
void v
|
||||||
|
.whereNull(`${TableName.Relay}.healthAlertedAt`)
|
||||||
|
.orWhere(`${TableName.Relay}.healthAlertedAt`, "<", db.ref("heartbeat").withSchema(TableName.Relay));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (limit) void query.limit(limit);
|
||||||
|
if (offset) void query.offset(offset);
|
||||||
|
if (sort) {
|
||||||
|
void query.orderBy(sort.map(([column, order, nulls]) => ({ column: column as string, order, nulls })));
|
||||||
|
}
|
||||||
|
|
||||||
|
const docs = await query;
|
||||||
|
return docs;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: `${TableName.Relay}: Find` });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...orm, find };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,11 +2,15 @@ import { isIP } from "node:net";
|
|||||||
|
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
|
import { CronJob } from "cron";
|
||||||
|
|
||||||
import { TRelays } from "@app/db/schemas";
|
import { OrgMembershipRole, TRelays } from "@app/db/schemas";
|
||||||
import { PgSqlLock } from "@app/keystore/keystore";
|
import { PgSqlLock } from "@app/keystore/keystore";
|
||||||
import { crypto } from "@app/lib/crypto";
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
|
import { groupBy } from "@app/lib/fn";
|
||||||
|
import { createRelayConnection } from "@app/lib/gateway-v2/gateway-v2";
|
||||||
|
import { logger } from "@app/lib/logger";
|
||||||
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
||||||
import { constructPemChainFromCerts, prependCertToPemChain } from "@app/services/certificate/certificate-fns";
|
import { constructPemChainFromCerts, prependCertToPemChain } from "@app/services/certificate/certificate-fns";
|
||||||
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
@@ -16,6 +20,11 @@ import {
|
|||||||
} from "@app/services/certificate-authority/certificate-authority-fns";
|
} from "@app/services/certificate-authority/certificate-authority-fns";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
|
import { TNotificationServiceFactory } from "@app/services/notification/notification-service";
|
||||||
|
import { NotificationType } from "@app/services/notification/notification-types";
|
||||||
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
|
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
import { verifyHostInputValidity } from "../dynamic-secret/dynamic-secret-fns";
|
import { verifyHostInputValidity } from "../dynamic-secret/dynamic-secret-fns";
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
@@ -39,7 +48,11 @@ export const relayServiceFactory = ({
|
|||||||
relayDAL,
|
relayDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
licenseService,
|
licenseService,
|
||||||
permissionService
|
permissionService,
|
||||||
|
orgDAL,
|
||||||
|
notificationService,
|
||||||
|
smtpService,
|
||||||
|
userDAL
|
||||||
}: {
|
}: {
|
||||||
instanceRelayConfigDAL: TInstanceRelayConfigDALFactory;
|
instanceRelayConfigDAL: TInstanceRelayConfigDALFactory;
|
||||||
orgRelayConfigDAL: TOrgRelayConfigDALFactory;
|
orgRelayConfigDAL: TOrgRelayConfigDALFactory;
|
||||||
@@ -47,6 +60,10 @@ export const relayServiceFactory = ({
|
|||||||
kmsService: TKmsServiceFactory;
|
kmsService: TKmsServiceFactory;
|
||||||
licenseService: TLicenseServiceFactory;
|
licenseService: TLicenseServiceFactory;
|
||||||
permissionService: TPermissionServiceFactory;
|
permissionService: TPermissionServiceFactory;
|
||||||
|
orgDAL: Pick<TOrgDALFactory, "findOrgMembersByRole">;
|
||||||
|
notificationService: Pick<TNotificationServiceFactory, "createUserNotifications">;
|
||||||
|
smtpService: Pick<TSmtpService, "sendMail">;
|
||||||
|
userDAL: Pick<TUserDALFactory, "find">;
|
||||||
}) => {
|
}) => {
|
||||||
const $getInstanceCAs = async () => {
|
const $getInstanceCAs = async () => {
|
||||||
const instanceConfig = await instanceRelayConfigDAL.transaction(async (tx) => {
|
const instanceConfig = await instanceRelayConfigDAL.transaction(async (tx) => {
|
||||||
@@ -1056,6 +1073,78 @@ export const relayServiceFactory = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const heartbeat = async ({
|
||||||
|
name,
|
||||||
|
identityId,
|
||||||
|
actorAuthMethod,
|
||||||
|
orgId
|
||||||
|
}: {
|
||||||
|
name: string;
|
||||||
|
identityId?: string;
|
||||||
|
actorAuthMethod?: ActorAuthMethod;
|
||||||
|
orgId?: string;
|
||||||
|
}) => {
|
||||||
|
const relay = await relayDAL.findOne({
|
||||||
|
name,
|
||||||
|
orgId: orgId ?? null
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!relay) {
|
||||||
|
throw new NotFoundError({ message: `Relay with name ${name} not found.` });
|
||||||
|
}
|
||||||
|
|
||||||
|
let clientOrgId: string;
|
||||||
|
let clientOrgName: string;
|
||||||
|
|
||||||
|
if (relay.orgId) {
|
||||||
|
if (!identityId || !orgId || relay.orgId !== orgId) {
|
||||||
|
throw new ForbiddenRequestError({
|
||||||
|
message: "You do not have permission to perform this action on this relay."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
|
ActorType.IDENTITY,
|
||||||
|
identityId,
|
||||||
|
orgId,
|
||||||
|
actorAuthMethod!,
|
||||||
|
orgId
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionRelayActions.CreateRelays,
|
||||||
|
OrgPermissionSubjects.Relay
|
||||||
|
);
|
||||||
|
clientOrgId = orgId;
|
||||||
|
clientOrgName = orgId;
|
||||||
|
} else {
|
||||||
|
clientOrgId = "00000000-0000-0000-0000-000000000000";
|
||||||
|
clientOrgName = "heartbeat";
|
||||||
|
}
|
||||||
|
|
||||||
|
const relayClientCredentials = await getCredentialsForClient({
|
||||||
|
relayId: relay.id,
|
||||||
|
orgId: clientOrgId,
|
||||||
|
orgName: clientOrgName,
|
||||||
|
gatewayId: "00000000-0000-0000-0000-000000000000",
|
||||||
|
gatewayName: "heartbeat",
|
||||||
|
duration: 60 * 1000 // 1 minute
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await createRelayConnection({
|
||||||
|
relayHost: relayClientCredentials.relayHost,
|
||||||
|
clientCertificate: relayClientCredentials.clientCertificate,
|
||||||
|
clientPrivateKey: relayClientCredentials.clientPrivateKey,
|
||||||
|
serverCertificateChain: relayClientCredentials.serverCertificateChain
|
||||||
|
});
|
||||||
|
|
||||||
|
await relayDAL.updateById(relay.id, { heartbeat: new Date() });
|
||||||
|
} catch (err) {
|
||||||
|
const error = err as Error;
|
||||||
|
throw new BadRequestError({ message: `Relay ${name} is not reachable: ${error.message}` });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getRelays = async ({
|
const getRelays = async ({
|
||||||
actorId,
|
actorId,
|
||||||
actor,
|
actor,
|
||||||
@@ -1120,11 +1209,99 @@ export const relayServiceFactory = ({
|
|||||||
return deletedRelay;
|
return deletedRelay;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const $healthcheckNotify = async () => {
|
||||||
|
const unhealthyRelays = await relayDAL.find({
|
||||||
|
isHeartbeatStale: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (unhealthyRelays.length === 0) return;
|
||||||
|
|
||||||
|
logger.warn(
|
||||||
|
{ relayIds: unhealthyRelays.map((g) => g.id) },
|
||||||
|
"Found relays with last heartbeat over an hour ago. Sending notifications."
|
||||||
|
);
|
||||||
|
|
||||||
|
const relaysByOrg = groupBy(unhealthyRelays, (r) => r.orgId ?? "instance");
|
||||||
|
|
||||||
|
for await (const [orgId, relays] of Object.entries(relaysByOrg)) {
|
||||||
|
try {
|
||||||
|
if (orgId === "instance") {
|
||||||
|
const superAdmins = await userDAL.find({
|
||||||
|
superAdmin: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const recipients = superAdmins.map((admin) => admin.email).filter((v): v is string => !!v);
|
||||||
|
|
||||||
|
if (recipients.length > 0) {
|
||||||
|
const relayNames = relays.map((r) => `"${r.name}"`).join(", ");
|
||||||
|
await smtpService.sendMail({
|
||||||
|
recipients,
|
||||||
|
subjectLine: "Relay Health Alert",
|
||||||
|
substitutions: {
|
||||||
|
type: "instance-relay",
|
||||||
|
names: relayNames
|
||||||
|
},
|
||||||
|
template: SmtpTemplates.HealthAlert
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const admins = await orgDAL.findOrgMembersByRole(orgId, OrgMembershipRole.Admin);
|
||||||
|
if (admins.length === 0) {
|
||||||
|
// eslint-disable-next-line no-continue
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const relayNames = relays.map((r) => `"${r.name}"`).join(", ");
|
||||||
|
const body = `The following relay(s) in your organization may be offline as they haven't reported a heartbeat in over an hour: ${relayNames}. Please check their status.`;
|
||||||
|
|
||||||
|
await notificationService.createUserNotifications(
|
||||||
|
admins.map((admin) => ({
|
||||||
|
userId: admin.user.id,
|
||||||
|
orgId,
|
||||||
|
type: NotificationType.RELAY_HEALTH_ALERT,
|
||||||
|
title: "Relay Health Alert",
|
||||||
|
body,
|
||||||
|
link: "/organization/networking"
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
await smtpService.sendMail({
|
||||||
|
recipients: admins.map((admin) => admin.user.email).filter((v): v is string => !!v),
|
||||||
|
subjectLine: "Relay Health Alert",
|
||||||
|
substitutions: {
|
||||||
|
type: "relay",
|
||||||
|
names: relayNames
|
||||||
|
},
|
||||||
|
template: SmtpTemplates.HealthAlert
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(relays.map((r) => relayDAL.updateById(r.id, { healthAlertedAt: new Date() })));
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error, `Failed to send relay health notifications for organization [orgId=${orgId}]`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const initializeHealthcheckNotify = async () => {
|
||||||
|
logger.info("Setting up background notification process for relay health-checks");
|
||||||
|
|
||||||
|
await $healthcheckNotify();
|
||||||
|
|
||||||
|
// run every 5 minutes
|
||||||
|
const job = new CronJob("*/5 * * * *", $healthcheckNotify);
|
||||||
|
job.start();
|
||||||
|
|
||||||
|
return job;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
registerRelay,
|
registerRelay,
|
||||||
getCredentialsForGateway,
|
getCredentialsForGateway,
|
||||||
getCredentialsForClient,
|
getCredentialsForClient,
|
||||||
getRelays,
|
getRelays,
|
||||||
deleteRelay
|
deleteRelay,
|
||||||
|
heartbeat,
|
||||||
|
initializeHealthcheckNotify
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Knex } from "knex";
|
|||||||
import RE2 from "re2";
|
import RE2 from "re2";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
AccessScope,
|
||||||
OrgMembershipRole,
|
OrgMembershipRole,
|
||||||
OrgMembershipStatus,
|
OrgMembershipStatus,
|
||||||
TableName,
|
TableName,
|
||||||
@@ -19,13 +20,13 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/
|
|||||||
import { AuthTokenType } from "@app/services/auth/auth-type";
|
import { AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||||
import { TokenType } from "@app/services/auth-token/auth-token-types";
|
import { TokenType } from "@app/services/auth-token/auth-token-types";
|
||||||
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
|
||||||
import { TIdentityMetadataDALFactory } from "@app/services/identity/identity-metadata-dal";
|
import { TIdentityMetadataDALFactory } from "@app/services/identity/identity-metadata-dal";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
|
import { TMembershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
||||||
|
import { TMembershipGroupDALFactory } from "@app/services/membership-group/membership-group-dal";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { getDefaultOrgMembershipRole } from "@app/services/org/org-role-fns";
|
import { getDefaultOrgMembershipRole } from "@app/services/org/org-role-fns";
|
||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
||||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||||
@@ -68,32 +69,30 @@ type TSamlConfigServiceFactoryDep = {
|
|||||||
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
|
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
|
||||||
>;
|
>;
|
||||||
identityMetadataDAL: Pick<TIdentityMetadataDALFactory, "delete" | "insertMany" | "transaction">;
|
identityMetadataDAL: Pick<TIdentityMetadataDALFactory, "delete" | "insertMany" | "transaction">;
|
||||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">;
|
membershipRoleDAL: Pick<TMembershipRoleDALFactory, "create">;
|
||||||
groupDAL: Pick<TGroupDALFactory, "create" | "findOne" | "find" | "transaction">;
|
|
||||||
userGroupMembershipDAL: Pick<
|
|
||||||
TUserGroupMembershipDALFactory,
|
|
||||||
"find" | "delete" | "transaction" | "insertMany" | "filterProjectsByUserMembership"
|
|
||||||
>;
|
|
||||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
|
||||||
projectDAL: Pick<TProjectDALFactory, "findById" | "findProjectGhostUser">;
|
|
||||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
|
||||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "findLatestProjectKey" | "insertMany">;
|
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
|
||||||
tokenService: Pick<TAuthTokenServiceFactory, "createTokenForUser">;
|
tokenService: Pick<TAuthTokenServiceFactory, "createTokenForUser">;
|
||||||
smtpService: Pick<TSmtpService, "sendMail">;
|
smtpService: Pick<TSmtpService, "sendMail">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
|
userGroupMembershipDAL: Pick<
|
||||||
|
TUserGroupMembershipDALFactory,
|
||||||
|
"find" | "delete" | "transaction" | "insertMany" | "filterProjectsByUserMembership"
|
||||||
|
>;
|
||||||
|
groupDAL: Pick<TGroupDALFactory, "create" | "findOne" | "find" | "transaction">;
|
||||||
|
projectDAL: Pick<TProjectDALFactory, "findById" | "findProjectGhostUser">;
|
||||||
|
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
||||||
|
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "findLatestProjectKey" | "insertMany">;
|
||||||
|
membershipGroupDAL: Pick<TMembershipGroupDALFactory, "find">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const samlConfigServiceFactory = ({
|
export const samlConfigServiceFactory = ({
|
||||||
samlConfigDAL,
|
samlConfigDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
userDAL,
|
userDAL,
|
||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupProjectDAL,
|
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
@@ -102,7 +101,9 @@ export const samlConfigServiceFactory = ({
|
|||||||
tokenService,
|
tokenService,
|
||||||
smtpService,
|
smtpService,
|
||||||
identityMetadataDAL,
|
identityMetadataDAL,
|
||||||
kmsService
|
kmsService,
|
||||||
|
membershipRoleDAL,
|
||||||
|
membershipGroupDAL
|
||||||
}: TSamlConfigServiceFactoryDep): TSamlConfigServiceFactory => {
|
}: TSamlConfigServiceFactoryDep): TSamlConfigServiceFactory => {
|
||||||
const parseSamlGroups = (groupsValue: string): string[] => {
|
const parseSamlGroups = (groupsValue: string): string[] => {
|
||||||
let samlGroups: string[] = [];
|
let samlGroups: string[] = [];
|
||||||
@@ -195,10 +196,10 @@ export const samlConfigServiceFactory = ({
|
|||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
groupProjectDAL,
|
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
|
membershipGroupDAL,
|
||||||
tx: transaction
|
tx: transaction
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -218,7 +219,7 @@ export const samlConfigServiceFactory = ({
|
|||||||
group,
|
group,
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupProjectDAL,
|
membershipGroupDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
tx: transaction
|
tx: transaction
|
||||||
});
|
});
|
||||||
@@ -506,26 +507,35 @@ export const samlConfigServiceFactory = ({
|
|||||||
const foundUser = await userDAL.findById(userAlias.userId, tx);
|
const foundUser = await userDAL.findById(userAlias.userId, tx);
|
||||||
const [orgMembership] = await orgDAL.findMembership(
|
const [orgMembership] = await orgDAL.findMembership(
|
||||||
{
|
{
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: foundUser.id,
|
[`${TableName.Membership}.actorUserId` as "actorUserId"]: userAlias.userId,
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||||
|
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!orgMembership) {
|
if (!orgMembership) {
|
||||||
const { role, roleId } = await getDefaultOrgMembershipRole(organization.defaultMembershipRole);
|
const { role, roleId } = await getDefaultOrgMembershipRole(organization.defaultMembershipRole);
|
||||||
|
|
||||||
await orgMembershipDAL.create(
|
const membership = await orgDAL.createMembership(
|
||||||
{
|
{
|
||||||
userId: userAlias.userId,
|
actorUserId: userAlias.userId,
|
||||||
inviteEmail: email,
|
inviteEmail: email,
|
||||||
orgId,
|
scopeOrgId: orgId,
|
||||||
role,
|
scope: AccessScope.Organization,
|
||||||
roleId,
|
status: OrgMembershipStatus.Accepted,
|
||||||
status: foundUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited,
|
|
||||||
isActive: true
|
isActive: true
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
await membershipRoleDAL.create(
|
||||||
|
{
|
||||||
|
membershipId: membership.id,
|
||||||
|
role,
|
||||||
|
customRoleId: roleId
|
||||||
|
},
|
||||||
|
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 && foundUser.isAccepted) {
|
||||||
await orgDAL.updateMembershipById(
|
await orgDAL.updateMembershipById(
|
||||||
@@ -606,8 +616,9 @@ export const samlConfigServiceFactory = ({
|
|||||||
|
|
||||||
const [orgMembership] = await orgDAL.findMembership(
|
const [orgMembership] = await orgDAL.findMembership(
|
||||||
{
|
{
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: newUser.id,
|
[`${TableName.Membership}.actorUserId` as "actorUserId"]: userAlias.userId,
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||||
|
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
);
|
);
|
||||||
@@ -617,15 +628,22 @@ export const samlConfigServiceFactory = ({
|
|||||||
|
|
||||||
const { role, roleId } = await getDefaultOrgMembershipRole(organization.defaultMembershipRole);
|
const { role, roleId } = await getDefaultOrgMembershipRole(organization.defaultMembershipRole);
|
||||||
|
|
||||||
await orgMembershipDAL.create(
|
const membership = await orgDAL.createMembership(
|
||||||
{
|
{
|
||||||
userId: newUser.id,
|
actorUserId: newUser.id,
|
||||||
inviteEmail: email,
|
scopeOrgId: orgId,
|
||||||
orgId,
|
scope: AccessScope.Organization,
|
||||||
role,
|
|
||||||
roleId,
|
|
||||||
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
|
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
|
||||||
isActive: true
|
isActive: true,
|
||||||
|
inviteEmail: email.toLowerCase()
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
await membershipRoleDAL.create(
|
||||||
|
{
|
||||||
|
membershipId: membership.id,
|
||||||
|
role,
|
||||||
|
customRoleId: roleId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,15 @@ import { ForbiddenError } from "@casl/ability";
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import { scimPatch } from "scim-patch";
|
import { scimPatch } from "scim-patch";
|
||||||
|
|
||||||
import { OrgMembershipRole, OrgMembershipStatus, TableName, TGroups, TOrgMemberships, TUsers } from "@app/db/schemas";
|
import {
|
||||||
|
AccessScope,
|
||||||
|
OrgMembershipRole,
|
||||||
|
OrgMembershipStatus,
|
||||||
|
TableName,
|
||||||
|
TGroups,
|
||||||
|
TMemberships,
|
||||||
|
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";
|
||||||
@@ -11,18 +19,19 @@ import { getConfig } from "@app/lib/config/env";
|
|||||||
import { crypto } from "@app/lib/crypto";
|
import { crypto } from "@app/lib/crypto";
|
||||||
import { BadRequestError, NotFoundError, ScimRequestError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, ScimRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
import { TAdditionalPrivilegeDALFactory } from "@app/services/additional-privilege/additional-privilege-dal";
|
||||||
import { AuthTokenType } from "@app/services/auth/auth-type";
|
import { AuthTokenType } from "@app/services/auth/auth-type";
|
||||||
import { TExternalGroupOrgRoleMappingDALFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-dal";
|
import { TExternalGroupOrgRoleMappingDALFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-dal";
|
||||||
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
import { TMembershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
||||||
|
import { TMembershipGroupDALFactory } from "@app/services/membership-group/membership-group-dal";
|
||||||
|
import { TMembershipUserDALFactory } from "@app/services/membership-user/membership-user-dal";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { deleteOrgMembershipFn } from "@app/services/org/org-fns";
|
import { deleteOrgMembershipsFn } from "@app/services/org/org-fns";
|
||||||
import { getDefaultOrgMembershipRole } from "@app/services/org/org-role-fns";
|
import { getDefaultOrgMembershipRole } from "@app/services/org/org-role-fns";
|
||||||
import { OrgAuthMethod } from "@app/services/org/org-types";
|
import { OrgAuthMethod } from "@app/services/org/org-types";
|
||||||
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
|
||||||
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
|
||||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
|
||||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
import { 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";
|
||||||
@@ -33,7 +42,6 @@ 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";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service-types";
|
import { TPermissionServiceFactory } from "../permission/permission-service-types";
|
||||||
import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal";
|
|
||||||
import { buildScimGroup, buildScimGroupList, buildScimUser, buildScimUserList, parseScimFilter } from "./scim-fns";
|
import { buildScimGroup, buildScimGroupList, buildScimUser, buildScimUserList, parseScimFilter } from "./scim-fns";
|
||||||
import { TScimGroup, TScimServiceFactory } from "./scim-types";
|
import { TScimGroup, TScimServiceFactory } from "./scim-types";
|
||||||
|
|
||||||
@@ -55,12 +63,8 @@ type TScimServiceFactoryDep = {
|
|||||||
| "updateMembershipById"
|
| "updateMembershipById"
|
||||||
| "findOrgById"
|
| "findOrgById"
|
||||||
>;
|
>;
|
||||||
orgMembershipDAL: Pick<
|
membershipUserDAL: TMembershipUserDALFactory;
|
||||||
TOrgMembershipDALFactory,
|
|
||||||
"find" | "findOne" | "create" | "updateById" | "findById" | "update"
|
|
||||||
>;
|
|
||||||
projectDAL: Pick<TProjectDALFactory, "find" | "findProjectGhostUser" | "findById">;
|
projectDAL: Pick<TProjectDALFactory, "find" | "findProjectGhostUser" | "findById">;
|
||||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find" | "delete" | "findProjectMembershipsByUserId">;
|
|
||||||
groupDAL: Pick<
|
groupDAL: Pick<
|
||||||
TGroupDALFactory,
|
TGroupDALFactory,
|
||||||
| "create"
|
| "create"
|
||||||
@@ -72,7 +76,8 @@ type TScimServiceFactoryDep = {
|
|||||||
| "updateById"
|
| "updateById"
|
||||||
| "update"
|
| "update"
|
||||||
>;
|
>;
|
||||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
membershipGroupDAL: Pick<TMembershipGroupDALFactory, "find" | "create">;
|
||||||
|
membershipRoleDAL: TMembershipRoleDALFactory;
|
||||||
userGroupMembershipDAL: Pick<
|
userGroupMembershipDAL: Pick<
|
||||||
TUserGroupMembershipDALFactory,
|
TUserGroupMembershipDALFactory,
|
||||||
| "find"
|
| "find"
|
||||||
@@ -88,8 +93,8 @@ type TScimServiceFactoryDep = {
|
|||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
smtpService: Pick<TSmtpService, "sendMail">;
|
smtpService: Pick<TSmtpService, "sendMail">;
|
||||||
projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
|
||||||
externalGroupOrgRoleMappingDAL: TExternalGroupOrgRoleMappingDALFactory;
|
externalGroupOrgRoleMappingDAL: TExternalGroupOrgRoleMappingDALFactory;
|
||||||
|
additionalPrivilegeDAL: TAdditionalPrivilegeDALFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const scimServiceFactory = ({
|
export const scimServiceFactory = ({
|
||||||
@@ -98,18 +103,18 @@ export const scimServiceFactory = ({
|
|||||||
userDAL,
|
userDAL,
|
||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectMembershipDAL,
|
|
||||||
groupDAL,
|
groupDAL,
|
||||||
groupProjectDAL,
|
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectUserAdditionalPrivilegeDAL,
|
|
||||||
smtpService,
|
smtpService,
|
||||||
externalGroupOrgRoleMappingDAL
|
externalGroupOrgRoleMappingDAL,
|
||||||
|
membershipGroupDAL,
|
||||||
|
membershipUserDAL,
|
||||||
|
membershipRoleDAL,
|
||||||
|
additionalPrivilegeDAL
|
||||||
}: TScimServiceFactoryDep): TScimServiceFactory => {
|
}: TScimServiceFactoryDep): TScimServiceFactory => {
|
||||||
const createScimToken: TScimServiceFactory["createScimToken"] = async ({
|
const createScimToken: TScimServiceFactory["createScimToken"] = async ({
|
||||||
actor,
|
actor,
|
||||||
@@ -244,8 +249,9 @@ export const scimServiceFactory = ({
|
|||||||
const getScimUser: TScimServiceFactory["getScimUser"] = async ({ orgMembershipId, orgId }) => {
|
const getScimUser: TScimServiceFactory["getScimUser"] = async ({ orgMembershipId, orgId }) => {
|
||||||
const [membership] = await orgDAL
|
const [membership] = await orgDAL
|
||||||
.findMembership({
|
.findMembership({
|
||||||
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
|
[`${TableName.Membership}.id` as "id"]: orgMembershipId,
|
||||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||||
|
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
throw new ScimRequestError({
|
throw new ScimRequestError({
|
||||||
@@ -322,13 +328,14 @@ export const scimServiceFactory = ({
|
|||||||
|
|
||||||
const { user: createdUser, orgMembership: createdOrgMembership } = await userDAL.transaction(async (tx) => {
|
const { user: createdUser, orgMembership: createdOrgMembership } = await userDAL.transaction(async (tx) => {
|
||||||
let user: TUsers | undefined;
|
let user: TUsers | undefined;
|
||||||
let orgMembership: TOrgMemberships;
|
let orgMembership: TMemberships;
|
||||||
if (userAlias) {
|
if (userAlias) {
|
||||||
user = await userDAL.findById(userAlias.userId, tx);
|
user = await userDAL.findById(userAlias.userId, tx);
|
||||||
orgMembership = await orgMembershipDAL.findOne(
|
orgMembership = await membershipUserDAL.findOne(
|
||||||
{
|
{
|
||||||
userId: user.id,
|
actorUserId: user.id,
|
||||||
orgId
|
scope: AccessScope.Organization,
|
||||||
|
scopeOrgId: orgId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -336,20 +343,27 @@ export const scimServiceFactory = ({
|
|||||||
if (!orgMembership) {
|
if (!orgMembership) {
|
||||||
const { role, roleId } = await getDefaultOrgMembershipRole(org.defaultMembershipRole);
|
const { role, roleId } = await getDefaultOrgMembershipRole(org.defaultMembershipRole);
|
||||||
|
|
||||||
orgMembership = await orgMembershipDAL.create(
|
orgMembership = await membershipUserDAL.create(
|
||||||
{
|
{
|
||||||
userId: userAlias.userId,
|
actorUserId: userAlias.userId,
|
||||||
inviteEmail: email.toLowerCase(),
|
inviteEmail: email.toLowerCase(),
|
||||||
orgId,
|
scopeOrgId: orgId,
|
||||||
role,
|
scope: AccessScope.Organization,
|
||||||
roleId,
|
|
||||||
status: user.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited, // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
|
status: 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
|
||||||
isActive: true
|
isActive: true
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
await membershipRoleDAL.create(
|
||||||
|
{
|
||||||
|
membershipId: orgMembership.id,
|
||||||
|
role,
|
||||||
|
customRoleId: roleId
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
} else if (orgMembership.status === OrgMembershipStatus.Invited && user.isAccepted) {
|
} else if (orgMembership.status === OrgMembershipStatus.Invited && user.isAccepted) {
|
||||||
orgMembership = await orgMembershipDAL.updateById(
|
orgMembership = await membershipUserDAL.updateById(
|
||||||
orgMembership.id,
|
orgMembership.id,
|
||||||
{
|
{
|
||||||
status: OrgMembershipStatus.Accepted
|
status: OrgMembershipStatus.Accepted
|
||||||
@@ -401,8 +415,9 @@ export const scimServiceFactory = ({
|
|||||||
|
|
||||||
const [foundOrgMembership] = await orgDAL.findMembership(
|
const [foundOrgMembership] = await orgDAL.findMembership(
|
||||||
{
|
{
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: user.id,
|
[`${TableName.Membership}.actorUserId` as "actorUserId"]: user.id,
|
||||||
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||||
|
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization
|
||||||
},
|
},
|
||||||
{ tx }
|
{ tx }
|
||||||
);
|
);
|
||||||
@@ -412,18 +427,25 @@ export const scimServiceFactory = ({
|
|||||||
if (!orgMembership) {
|
if (!orgMembership) {
|
||||||
const { role, roleId } = await getDefaultOrgMembershipRole(org.defaultMembershipRole);
|
const { role, roleId } = await getDefaultOrgMembershipRole(org.defaultMembershipRole);
|
||||||
|
|
||||||
orgMembership = await orgMembershipDAL.create(
|
orgMembership = await membershipUserDAL.create(
|
||||||
{
|
{
|
||||||
userId: user.id,
|
actorUserId: user.id,
|
||||||
inviteEmail: email.toLowerCase(),
|
inviteEmail: email.toLowerCase(),
|
||||||
orgId,
|
scopeOrgId: orgId,
|
||||||
role,
|
scope: AccessScope.Organization,
|
||||||
roleId,
|
|
||||||
status: user.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited, // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
|
status: 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
|
||||||
isActive: true
|
isActive: true
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
await membershipRoleDAL.create(
|
||||||
|
{
|
||||||
|
membershipId: orgMembership.id,
|
||||||
|
role,
|
||||||
|
customRoleId: roleId
|
||||||
|
},
|
||||||
|
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 && user.isAccepted) {
|
} else if (orgMembership.status === OrgMembershipStatus.Invited && user.isAccepted) {
|
||||||
orgMembership = await orgDAL.updateMembershipById(
|
orgMembership = await orgDAL.updateMembershipById(
|
||||||
@@ -475,8 +497,9 @@ export const scimServiceFactory = ({
|
|||||||
|
|
||||||
const [membership] = await orgDAL
|
const [membership] = await orgDAL
|
||||||
.findMembership({
|
.findMembership({
|
||||||
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
|
[`${TableName.Membership}.id` as "id"]: orgMembershipId,
|
||||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||||
|
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
throw new ScimRequestError({
|
throw new ScimRequestError({
|
||||||
@@ -485,7 +508,7 @@ export const scimServiceFactory = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!membership)
|
if (!membership || !membership.actorUserId)
|
||||||
throw new ScimRequestError({
|
throw new ScimRequestError({
|
||||||
detail: "User not found",
|
detail: "User not found",
|
||||||
status: 404
|
status: 404
|
||||||
@@ -514,7 +537,7 @@ export const scimServiceFactory = ({
|
|||||||
org.orgAuthMethod === OrgAuthMethod.OIDC ? serverCfg.trustOidcEmails : serverCfg.trustSamlEmails;
|
org.orgAuthMethod === OrgAuthMethod.OIDC ? serverCfg.trustOidcEmails : serverCfg.trustSamlEmails;
|
||||||
|
|
||||||
await userDAL.transaction(async (tx) => {
|
await userDAL.transaction(async (tx) => {
|
||||||
await orgMembershipDAL.updateById(
|
await membershipUserDAL.updateById(
|
||||||
membership.id,
|
membership.id,
|
||||||
{
|
{
|
||||||
isActive: scimUser.active
|
isActive: scimUser.active
|
||||||
@@ -523,7 +546,7 @@ export const scimServiceFactory = ({
|
|||||||
);
|
);
|
||||||
const hasEmailChanged = scimUser.emails[0].value !== membership.email;
|
const hasEmailChanged = scimUser.emails[0].value !== membership.email;
|
||||||
await userDAL.updateById(
|
await userDAL.updateById(
|
||||||
membership.userId,
|
membership.actorUserId as string,
|
||||||
{
|
{
|
||||||
firstName: scimUser.name.givenName,
|
firstName: scimUser.name.givenName,
|
||||||
email: scimUser.emails[0].value.toLowerCase(),
|
email: scimUser.emails[0].value.toLowerCase(),
|
||||||
@@ -556,8 +579,9 @@ export const scimServiceFactory = ({
|
|||||||
|
|
||||||
const [membership] = await orgDAL
|
const [membership] = await orgDAL
|
||||||
.findMembership({
|
.findMembership({
|
||||||
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
|
[`${TableName.Membership}.id` as "id"]: orgMembershipId,
|
||||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||||
|
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
throw new ScimRequestError({
|
throw new ScimRequestError({
|
||||||
@@ -566,7 +590,7 @@ export const scimServiceFactory = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!membership)
|
if (!membership || !membership.actorUserId)
|
||||||
throw new ScimRequestError({
|
throw new ScimRequestError({
|
||||||
detail: "User not found",
|
detail: "User not found",
|
||||||
status: 404
|
status: 404
|
||||||
@@ -587,7 +611,7 @@ export const scimServiceFactory = ({
|
|||||||
{
|
{
|
||||||
orgId,
|
orgId,
|
||||||
aliasType: org.orgAuthMethod === OrgAuthMethod.OIDC ? UserAliasType.OIDC : UserAliasType.SAML,
|
aliasType: org.orgAuthMethod === OrgAuthMethod.OIDC ? UserAliasType.OIDC : UserAliasType.SAML,
|
||||||
userId: membership.userId
|
userId: membership.actorUserId as string
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
externalId
|
externalId
|
||||||
@@ -595,7 +619,7 @@ export const scimServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
await orgMembershipDAL.updateById(
|
await membershipUserDAL.updateById(
|
||||||
membership.id,
|
membership.id,
|
||||||
{
|
{
|
||||||
isActive: active
|
isActive: active
|
||||||
@@ -603,7 +627,7 @@ export const scimServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
await userDAL.updateById(
|
await userDAL.updateById(
|
||||||
membership.userId,
|
membership.actorUserId!,
|
||||||
{
|
{
|
||||||
firstName,
|
firstName,
|
||||||
email: email?.toLowerCase(),
|
email: email?.toLowerCase(),
|
||||||
@@ -628,8 +652,9 @@ export const scimServiceFactory = ({
|
|||||||
|
|
||||||
const deleteScimUser: TScimServiceFactory["deleteScimUser"] = async ({ orgMembershipId, orgId }) => {
|
const deleteScimUser: TScimServiceFactory["deleteScimUser"] = async ({ orgMembershipId, orgId }) => {
|
||||||
const [membership] = await orgDAL.findMembership({
|
const [membership] = await orgDAL.findMembership({
|
||||||
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
|
[`${TableName.Membership}.id` as "id"]: orgMembershipId,
|
||||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||||
|
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!membership)
|
if (!membership)
|
||||||
@@ -645,15 +670,17 @@ export const scimServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await deleteOrgMembershipFn({
|
await deleteOrgMembershipsFn({
|
||||||
orgMembershipId: membership.id,
|
orgMembershipIds: [membership.id],
|
||||||
orgId: membership.orgId,
|
orgId: membership.scopeOrgId,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
projectMembershipDAL,
|
|
||||||
projectUserAdditionalPrivilegeDAL,
|
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
licenseService
|
licenseService,
|
||||||
|
membershipUserDAL,
|
||||||
|
membershipRoleDAL,
|
||||||
|
userGroupMembershipDAL,
|
||||||
|
additionalPrivilegeDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
return {}; // intentionally return empty object upon success
|
return {}; // intentionally return empty object upon success
|
||||||
@@ -750,8 +777,9 @@ export const scimServiceFactory = ({
|
|||||||
if (!externalGroupMapping) return;
|
if (!externalGroupMapping) return;
|
||||||
|
|
||||||
// only get org memberships that are new (invites)
|
// only get org memberships that are new (invites)
|
||||||
const newOrgMemberships = await orgMembershipDAL.find({
|
const newOrgMemberships = await membershipUserDAL.find({
|
||||||
status: "invited",
|
status: "invited",
|
||||||
|
scope: AccessScope.Organization,
|
||||||
$in: {
|
$in: {
|
||||||
id: members.map((member) => member.value)
|
id: members.map((member) => member.value)
|
||||||
}
|
}
|
||||||
@@ -760,15 +788,15 @@ export const scimServiceFactory = ({
|
|||||||
if (!newOrgMemberships.length) return;
|
if (!newOrgMemberships.length) return;
|
||||||
|
|
||||||
// set new membership roles to group mapping value
|
// set new membership roles to group mapping value
|
||||||
await orgMembershipDAL.update(
|
await membershipRoleDAL.update(
|
||||||
{
|
{
|
||||||
$in: {
|
$in: {
|
||||||
id: newOrgMemberships.map((membership) => membership.id)
|
membershipId: newOrgMemberships.map((membership) => membership.id)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: externalGroupMapping.role,
|
role: externalGroupMapping.role,
|
||||||
roleId: externalGroupMapping.roleId
|
customRoleId: externalGroupMapping.roleId
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -821,8 +849,26 @@ export const scimServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const groupMembership = await membershipGroupDAL.create(
|
||||||
|
{
|
||||||
|
scope: AccessScope.Organization,
|
||||||
|
actorGroupId: group.id,
|
||||||
|
scopeOrgId: orgId
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
await membershipRoleDAL.create(
|
||||||
|
{
|
||||||
|
membershipId: groupMembership.id,
|
||||||
|
role: OrgMembershipRole.NoAccess
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
if (members && members.length) {
|
if (members && members.length) {
|
||||||
const orgMemberships = await orgMembershipDAL.find({
|
const orgMemberships = await membershipUserDAL.find({
|
||||||
|
scope: AccessScope.Organization,
|
||||||
$in: {
|
$in: {
|
||||||
id: members.map((member) => member.value)
|
id: members.map((member) => member.value)
|
||||||
}
|
}
|
||||||
@@ -830,14 +876,14 @@ export const scimServiceFactory = ({
|
|||||||
|
|
||||||
const newMembers = await addUsersToGroupByUserIds({
|
const newMembers = await addUsersToGroupByUserIds({
|
||||||
group,
|
group,
|
||||||
userIds: orgMemberships.map((membership) => membership.userId as string),
|
userIds: orgMemberships.map((membership) => membership.actorUserId as string),
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
groupProjectDAL,
|
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
|
membershipGroupDAL,
|
||||||
tx
|
tx
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -850,9 +896,10 @@ export const scimServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const orgMemberships = await orgDAL.findMembership({
|
const orgMemberships = await orgDAL.findMembership({
|
||||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId,
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||||
|
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization,
|
||||||
$in: {
|
$in: {
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: newGroup.newMembers.map((member) => member.id)
|
[`${TableName.Membership}.actorUserId` as "actorUserId"]: newGroup.newMembers.map((member) => member.id)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -895,9 +942,10 @@ export const scimServiceFactory = ({
|
|||||||
.then((g) => g.members);
|
.then((g) => g.members);
|
||||||
|
|
||||||
const orgMemberships = await orgDAL.findMembership({
|
const orgMemberships = await orgDAL.findMembership({
|
||||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId,
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||||
|
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization,
|
||||||
$in: {
|
$in: {
|
||||||
[`${TableName.OrgMembership}.userId` as "userId"]: users
|
[`${TableName.Membership}.actorUserId` as "actorUserId"]: users
|
||||||
.filter((user) => user.isPartOfGroup)
|
.filter((user) => user.isPartOfGroup)
|
||||||
.map((user) => user.id)
|
.map((user) => user.id)
|
||||||
}
|
}
|
||||||
@@ -933,10 +981,10 @@ export const scimServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updatedGroup = await groupDAL.transaction(async (tx) => {
|
const updatedGroup = await groupDAL.transaction(async (tx) => {
|
||||||
if (group.name !== displayName) {
|
if (group?.name !== displayName) {
|
||||||
await externalGroupOrgRoleMappingDAL.update(
|
await externalGroupOrgRoleMappingDAL.update(
|
||||||
{
|
{
|
||||||
groupName: group.name,
|
groupName: group?.name,
|
||||||
orgId
|
orgId
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -958,14 +1006,16 @@ export const scimServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const orgMemberships = members.length
|
const orgMemberships = members.length
|
||||||
? await orgMembershipDAL.find({
|
? await membershipUserDAL.find({
|
||||||
|
[`${TableName.Membership}.scopeOrgId` as "scopeOrgId"]: orgId,
|
||||||
|
[`${TableName.Membership}.scope` as "scope"]: AccessScope.Organization,
|
||||||
$in: {
|
$in: {
|
||||||
id: members.map((member) => member.value)
|
id: members.map((member) => member.value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const membersIdsSet = new Set(orgMemberships.map((orgMembership) => orgMembership.userId));
|
const membersIdsSet = new Set(orgMemberships.map((orgMembership) => orgMembership.actorUserId as string));
|
||||||
const userGroupMembers = await userGroupMembershipDAL.find({
|
const userGroupMembers = await userGroupMembershipDAL.find({
|
||||||
groupId: group.id
|
groupId: group.id
|
||||||
});
|
});
|
||||||
@@ -978,20 +1028,20 @@ export const scimServiceFactory = ({
|
|||||||
const allMembersUserIds = directMemberUserIds.concat(pendingGroupAdditionsUserIds);
|
const allMembersUserIds = directMemberUserIds.concat(pendingGroupAdditionsUserIds);
|
||||||
const allMembersUserIdsSet = new Set(allMembersUserIds);
|
const allMembersUserIdsSet = new Set(allMembersUserIds);
|
||||||
|
|
||||||
const toAddUserIds = orgMemberships.filter((member) => !allMembersUserIdsSet.has(member.userId as string));
|
const toAddUserIds = orgMemberships.filter((member) => !allMembersUserIdsSet.has(member.actorUserId as string));
|
||||||
const toRemoveUserIds = allMembersUserIds.filter((userId) => !membersIdsSet.has(userId));
|
const toRemoveUserIds = allMembersUserIds.filter((userId) => !membersIdsSet.has(userId));
|
||||||
|
|
||||||
if (toAddUserIds.length) {
|
if (toAddUserIds.length) {
|
||||||
await addUsersToGroupByUserIds({
|
await addUsersToGroupByUserIds({
|
||||||
group,
|
group,
|
||||||
userIds: toAddUserIds.map((member) => member.userId as string),
|
userIds: toAddUserIds.map((member) => member.actorUserId as string),
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
groupProjectDAL,
|
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
|
membershipGroupDAL,
|
||||||
tx
|
tx
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1002,7 +1052,7 @@ export const scimServiceFactory = ({
|
|||||||
userIds: toRemoveUserIds,
|
userIds: toRemoveUserIds,
|
||||||
userDAL,
|
userDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupProjectDAL,
|
membershipGroupDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
tx
|
tx
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import { Knex } from "knex";
|
|||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import {
|
import {
|
||||||
|
AccessScope,
|
||||||
SecretApprovalRequestsSchema,
|
SecretApprovalRequestsSchema,
|
||||||
TableName,
|
TableName,
|
||||||
TOrgMemberships,
|
TMemberships,
|
||||||
TSecretApprovalRequests,
|
TSecretApprovalRequests,
|
||||||
TSecretApprovalRequestsSecrets,
|
TSecretApprovalRequestsSecrets,
|
||||||
TUserGroupMembership,
|
TUserGroupMembership,
|
||||||
@@ -36,6 +37,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
.where(filter)
|
.where(filter)
|
||||||
.join(TableName.SecretFolder, `${TableName.SecretApprovalRequest}.folderId`, `${TableName.SecretFolder}.id`)
|
.join(TableName.SecretFolder, `${TableName.SecretApprovalRequest}.folderId`, `${TableName.SecretFolder}.id`)
|
||||||
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
|
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
|
||||||
|
.join(TableName.Project, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
|
||||||
.join(
|
.join(
|
||||||
TableName.SecretApprovalPolicy,
|
TableName.SecretApprovalPolicy,
|
||||||
`${TableName.SecretApprovalRequest}.policyId`,
|
`${TableName.SecretApprovalRequest}.policyId`,
|
||||||
@@ -109,24 +111,22 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
`secretApprovalReviewerUser.id`
|
`secretApprovalReviewerUser.id`
|
||||||
)
|
)
|
||||||
|
|
||||||
.leftJoin<TOrgMemberships>(
|
.leftJoin<TMemberships>(db(TableName.Membership).as("approverOrgMembership"), (qb) => {
|
||||||
db(TableName.OrgMembership).as("approverOrgMembership"),
|
qb.on(`${TableName.SecretApprovalPolicyApprover}.approverUserId`, `approverOrgMembership.actorUserId`)
|
||||||
`${TableName.SecretApprovalPolicyApprover}.approverUserId`,
|
.andOn(`approverOrgMembership.scopeOrgId`, `${TableName.Project}.orgId`)
|
||||||
`approverOrgMembership.userId`
|
.andOn(`approverOrgMembership.scope`, db.raw("?", [AccessScope.Organization]));
|
||||||
)
|
})
|
||||||
|
|
||||||
.leftJoin<TOrgMemberships>(
|
|
||||||
db(TableName.OrgMembership).as("approverGroupOrgMembership"),
|
|
||||||
`secretApprovalPolicyGroupApproverUser.id`,
|
|
||||||
`approverGroupOrgMembership.userId`
|
|
||||||
)
|
|
||||||
|
|
||||||
.leftJoin<TOrgMemberships>(
|
|
||||||
db(TableName.OrgMembership).as("reviewerOrgMembership"),
|
|
||||||
`${TableName.SecretApprovalRequestReviewer}.reviewerUserId`,
|
|
||||||
`reviewerOrgMembership.userId`
|
|
||||||
)
|
|
||||||
|
|
||||||
|
.leftJoin<TMemberships>(db(TableName.Membership).as("approverGroupOrgMembership"), (qb) => {
|
||||||
|
qb.on(`secretApprovalPolicyGroupApproverUser.id`, `approverGroupOrgMembership.actorUserId`)
|
||||||
|
.andOn(`approverGroupOrgMembership.scopeOrgId`, `${TableName.Project}.orgId`)
|
||||||
|
.andOn(`approverGroupOrgMembership.scope`, db.raw("?", [AccessScope.Organization]));
|
||||||
|
})
|
||||||
|
.leftJoin<TMemberships>(db(TableName.Membership).as("reviewerOrgMembership"), (qb) => {
|
||||||
|
qb.on(`${TableName.SecretApprovalRequestReviewer}.reviewerUserId`, `reviewerOrgMembership.actorUserId`)
|
||||||
|
.andOn(`reviewerOrgMembership.scopeOrgId`, `${TableName.Project}.orgId`)
|
||||||
|
.andOn(`reviewerOrgMembership.scope`, db.raw("?", [AccessScope.Organization]));
|
||||||
|
})
|
||||||
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
.select(selectAllTableCols(TableName.SecretApprovalRequest))
|
||||||
.select(
|
.select(
|
||||||
tx.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover),
|
tx.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover),
|
||||||
|
|||||||
@@ -44,10 +44,7 @@ type TSshHostGroupServiceFactoryDep = {
|
|||||||
sshHostLoginUserDAL: Pick<TSshHostLoginUserDALFactory, "create" | "transaction" | "delete">;
|
sshHostLoginUserDAL: Pick<TSshHostLoginUserDALFactory, "create" | "transaction" | "delete">;
|
||||||
sshHostLoginUserMappingDAL: Pick<TSshHostLoginUserMappingDALFactory, "insertMany">;
|
sshHostLoginUserMappingDAL: Pick<TSshHostLoginUserMappingDALFactory, "insertMany">;
|
||||||
userDAL: Pick<TUserDALFactory, "find">;
|
userDAL: Pick<TUserDALFactory, "find">;
|
||||||
permissionService: Pick<
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "checkGroupProjectPermission">;
|
||||||
TPermissionServiceFactory,
|
|
||||||
"getProjectPermission" | "getUserProjectPermission" | "checkGroupProjectPermission"
|
|
||||||
>;
|
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
groupDAL: Pick<TGroupDALFactory, "findGroupsByProjectId">;
|
groupDAL: Pick<TGroupDALFactory, "findGroupsByProjectId">;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Knex } from "knex";
|
|||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
import { ProjectPermissionSshHostActions, ProjectPermissionSub } from "../permission/project-permission";
|
import { ProjectPermissionSshHostActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||||
import { TCreateSshLoginMappingsDTO } from "./ssh-host-types";
|
import { TCreateSshLoginMappingsDTO } from "./ssh-host-types";
|
||||||
@@ -59,11 +60,12 @@ export const createSshLoginMappings = async ({
|
|||||||
|
|
||||||
for await (const user of users) {
|
for await (const user of users) {
|
||||||
// check that each user has access to the SSH project
|
// check that each user has access to the SSH project
|
||||||
await permissionService.getUserProjectPermission({
|
await permissionService.getProjectPermission({
|
||||||
userId: user.id,
|
actor: ActorType.USER,
|
||||||
|
actorId: user.id,
|
||||||
projectId,
|
projectId,
|
||||||
authMethod: actorAuthMethod,
|
actorAuthMethod,
|
||||||
userOrgId: actorOrgId,
|
actorOrgId,
|
||||||
actionProjectType: ActionProjectType.SSH
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,10 +64,7 @@ type TSshHostServiceFactoryDep = {
|
|||||||
>;
|
>;
|
||||||
sshHostLoginUserDAL: TSshHostLoginUserDALFactory;
|
sshHostLoginUserDAL: TSshHostLoginUserDALFactory;
|
||||||
sshHostLoginUserMappingDAL: TSshHostLoginUserMappingDALFactory;
|
sshHostLoginUserMappingDAL: TSshHostLoginUserMappingDALFactory;
|
||||||
permissionService: Pick<
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "checkGroupProjectPermission">;
|
||||||
TPermissionServiceFactory,
|
|
||||||
"getProjectPermission" | "getUserProjectPermission" | "checkGroupProjectPermission"
|
|
||||||
>;
|
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ type BaseCreateSshLoginMappingsDTO = {
|
|||||||
sshHostLoginUserDAL: Pick<TSshHostLoginUserDALFactory, "create" | "transaction">;
|
sshHostLoginUserDAL: Pick<TSshHostLoginUserDALFactory, "create" | "transaction">;
|
||||||
sshHostLoginUserMappingDAL: Pick<TSshHostLoginUserMappingDALFactory, "insertMany">;
|
sshHostLoginUserMappingDAL: Pick<TSshHostLoginUserMappingDALFactory, "insertMany">;
|
||||||
userDAL: Pick<TUserDALFactory, "find">;
|
userDAL: Pick<TUserDALFactory, "find">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getUserProjectPermission" | "checkGroupProjectPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "checkGroupProjectPermission">;
|
||||||
groupDAL: Pick<TGroupDALFactory, "findGroupsByProjectId">;
|
groupDAL: Pick<TGroupDALFactory, "findGroupsByProjectId">;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
actorAuthMethod: ActorAuthMethod;
|
actorAuthMethod: ActorAuthMethod;
|
||||||
|
|||||||
@@ -53,3 +53,53 @@ export const titleCaseToCamelCase = (obj: unknown): unknown => {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const deepEqual = (obj1: unknown, obj2: unknown): boolean => {
|
||||||
|
if (obj1 === obj2) return true;
|
||||||
|
|
||||||
|
if (obj1 === null || obj2 === null || obj1 === undefined || obj2 === undefined) {
|
||||||
|
return obj1 === obj2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj1 !== typeof obj2) return false;
|
||||||
|
|
||||||
|
if (typeof obj1 !== "object") return obj1 === obj2;
|
||||||
|
|
||||||
|
if (Array.isArray(obj1) !== Array.isArray(obj2)) return false;
|
||||||
|
|
||||||
|
if (Array.isArray(obj1)) {
|
||||||
|
const arr1 = obj1 as unknown[];
|
||||||
|
const arr2 = obj2 as unknown[];
|
||||||
|
if (arr1.length !== arr2.length) return false;
|
||||||
|
return arr1.every((val, idx) => deepEqual(val, arr2[idx]));
|
||||||
|
}
|
||||||
|
|
||||||
|
const keys1 = Object.keys(obj1 as Record<string, unknown>).sort();
|
||||||
|
const keys2 = Object.keys(obj2 as Record<string, unknown>).sort();
|
||||||
|
|
||||||
|
if (keys1.length !== keys2.length) return false;
|
||||||
|
if (keys1.some((key, idx) => key !== keys2[idx])) return false;
|
||||||
|
|
||||||
|
return keys1.every((key) =>
|
||||||
|
deepEqual((obj1 as Record<string, unknown>)[key], (obj2 as Record<string, unknown>)[key])
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deepEqualSkipFields = (obj1: unknown, obj2: unknown, skipFields: string[] = []): boolean => {
|
||||||
|
if (skipFields.length === 0) {
|
||||||
|
return deepEqual(obj1, obj2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj1 !== "object" || typeof obj2 !== "object" || obj1 === null || obj2 === null) {
|
||||||
|
return deepEqual(obj1, obj2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filtered1 = Object.fromEntries(
|
||||||
|
Object.entries(obj1 as Record<string, unknown>).filter(([key]) => !skipFields.includes(key))
|
||||||
|
);
|
||||||
|
const filtered2 = Object.fromEntries(
|
||||||
|
Object.entries(obj2 as Record<string, unknown>).filter(([key]) => !skipFields.includes(key))
|
||||||
|
);
|
||||||
|
|
||||||
|
return deepEqual(filtered1, filtered2);
|
||||||
|
};
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ interface IGatewayRelayServer {
|
|||||||
getRelayError: () => string;
|
getRelayError: () => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const createRelayConnection = async ({
|
export const createRelayConnection = async ({
|
||||||
relayHost,
|
relayHost,
|
||||||
clientCertificate,
|
clientCertificate,
|
||||||
clientPrivateKey,
|
clientPrivateKey,
|
||||||
|
|||||||
@@ -50,9 +50,6 @@ import { hsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
|
|||||||
import { HsmModule } from "@app/ee/services/hsm/hsm-types";
|
import { HsmModule } from "@app/ee/services/hsm/hsm-types";
|
||||||
import { identityAuthTemplateDALFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-dal";
|
import { identityAuthTemplateDALFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-dal";
|
||||||
import { identityAuthTemplateServiceFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-service";
|
import { identityAuthTemplateServiceFactory } from "@app/ee/services/identity-auth-template/identity-auth-template-service";
|
||||||
import { identityProjectAdditionalPrivilegeDALFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-dal";
|
|
||||||
import { identityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service";
|
|
||||||
import { identityProjectAdditionalPrivilegeV2ServiceFactory } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-service";
|
|
||||||
import { kmipClientCertificateDALFactory } from "@app/ee/services/kmip/kmip-client-certificate-dal";
|
import { kmipClientCertificateDALFactory } from "@app/ee/services/kmip/kmip-client-certificate-dal";
|
||||||
import { kmipClientDALFactory } from "@app/ee/services/kmip/kmip-client-dal";
|
import { kmipClientDALFactory } from "@app/ee/services/kmip/kmip-client-dal";
|
||||||
import { kmipOperationServiceFactory } from "@app/ee/services/kmip/kmip-operation-service";
|
import { kmipOperationServiceFactory } from "@app/ee/services/kmip/kmip-operation-service";
|
||||||
@@ -79,8 +76,6 @@ import { permissionServiceFactory } from "@app/ee/services/permission/permission
|
|||||||
import { pitServiceFactory } from "@app/ee/services/pit/pit-service";
|
import { pitServiceFactory } from "@app/ee/services/pit/pit-service";
|
||||||
import { projectTemplateDALFactory } from "@app/ee/services/project-template/project-template-dal";
|
import { projectTemplateDALFactory } from "@app/ee/services/project-template/project-template-dal";
|
||||||
import { projectTemplateServiceFactory } from "@app/ee/services/project-template/project-template-service";
|
import { projectTemplateServiceFactory } from "@app/ee/services/project-template/project-template-service";
|
||||||
import { projectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal";
|
|
||||||
import { projectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-service";
|
|
||||||
import { rateLimitDALFactory } from "@app/ee/services/rate-limit/rate-limit-dal";
|
import { rateLimitDALFactory } from "@app/ee/services/rate-limit/rate-limit-dal";
|
||||||
import { rateLimitServiceFactory } from "@app/ee/services/rate-limit/rate-limit-service";
|
import { rateLimitServiceFactory } from "@app/ee/services/rate-limit/rate-limit-service";
|
||||||
import { instanceRelayConfigDalFactory } from "@app/ee/services/relay/instance-relay-config-dal";
|
import { instanceRelayConfigDalFactory } from "@app/ee/services/relay/instance-relay-config-dal";
|
||||||
@@ -147,6 +142,8 @@ import { TQueueServiceFactory } from "@app/queue";
|
|||||||
import { readLimit } from "@app/server/config/rateLimiter";
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
import { registerSecretScanningV2Webhooks } from "@app/server/plugins/secret-scanner-v2";
|
import { registerSecretScanningV2Webhooks } from "@app/server/plugins/secret-scanner-v2";
|
||||||
import { accessTokenQueueServiceFactory } from "@app/services/access-token-queue/access-token-queue";
|
import { accessTokenQueueServiceFactory } from "@app/services/access-token-queue/access-token-queue";
|
||||||
|
import { additionalPrivilegeDALFactory } from "@app/services/additional-privilege/additional-privilege-dal";
|
||||||
|
import { additionalPrivilegeServiceFactory } from "@app/services/additional-privilege/additional-privilege-service";
|
||||||
import { apiKeyDALFactory } from "@app/services/api-key/api-key-dal";
|
import { apiKeyDALFactory } from "@app/services/api-key/api-key-dal";
|
||||||
import { apiKeyServiceFactory } from "@app/services/api-key/api-key-service";
|
import { apiKeyServiceFactory } from "@app/services/api-key/api-key-service";
|
||||||
import { appConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
import { appConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||||
@@ -174,6 +171,7 @@ import { certificateTemplateDALFactory } from "@app/services/certificate-templat
|
|||||||
import { certificateTemplateEstConfigDALFactory } from "@app/services/certificate-template/certificate-template-est-config-dal";
|
import { certificateTemplateEstConfigDALFactory } from "@app/services/certificate-template/certificate-template-est-config-dal";
|
||||||
import { certificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
import { certificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
||||||
import { cmekServiceFactory } from "@app/services/cmek/cmek-service";
|
import { cmekServiceFactory } from "@app/services/cmek/cmek-service";
|
||||||
|
import { convertorServiceFactory } from "@app/services/convertor/convertor-service";
|
||||||
import { externalGroupOrgRoleMappingDALFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-dal";
|
import { externalGroupOrgRoleMappingDALFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-dal";
|
||||||
import { externalGroupOrgRoleMappingServiceFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-service";
|
import { externalGroupOrgRoleMappingServiceFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-service";
|
||||||
import { externalMigrationQueueFactory } from "@app/services/external-migration/external-migration-queue";
|
import { externalMigrationQueueFactory } from "@app/services/external-migration/external-migration-queue";
|
||||||
@@ -187,7 +185,6 @@ import { folderCommitChangesDALFactory } from "@app/services/folder-commit-chang
|
|||||||
import { folderTreeCheckpointDALFactory } from "@app/services/folder-tree-checkpoint/folder-tree-checkpoint-dal";
|
import { folderTreeCheckpointDALFactory } from "@app/services/folder-tree-checkpoint/folder-tree-checkpoint-dal";
|
||||||
import { folderTreeCheckpointResourcesDALFactory } from "@app/services/folder-tree-checkpoint-resources/folder-tree-checkpoint-resources-dal";
|
import { folderTreeCheckpointResourcesDALFactory } from "@app/services/folder-tree-checkpoint-resources/folder-tree-checkpoint-resources-dal";
|
||||||
import { groupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
import { groupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
||||||
import { groupProjectMembershipRoleDALFactory } from "@app/services/group-project/group-project-membership-role-dal";
|
|
||||||
import { groupProjectServiceFactory } from "@app/services/group-project/group-project-service";
|
import { groupProjectServiceFactory } from "@app/services/group-project/group-project-service";
|
||||||
import { identityDALFactory } from "@app/services/identity/identity-dal";
|
import { identityDALFactory } from "@app/services/identity/identity-dal";
|
||||||
import { identityMetadataDALFactory } from "@app/services/identity/identity-metadata-dal";
|
import { identityMetadataDALFactory } from "@app/services/identity/identity-metadata-dal";
|
||||||
@@ -214,7 +211,6 @@ import { identityOciAuthServiceFactory } from "@app/services/identity-oci-auth/i
|
|||||||
import { identityOidcAuthDALFactory } from "@app/services/identity-oidc-auth/identity-oidc-auth-dal";
|
import { identityOidcAuthDALFactory } from "@app/services/identity-oidc-auth/identity-oidc-auth-dal";
|
||||||
import { identityOidcAuthServiceFactory } from "@app/services/identity-oidc-auth/identity-oidc-auth-service";
|
import { identityOidcAuthServiceFactory } from "@app/services/identity-oidc-auth/identity-oidc-auth-service";
|
||||||
import { identityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
import { identityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||||
import { identityProjectMembershipRoleDALFactory } from "@app/services/identity-project/identity-project-membership-role-dal";
|
|
||||||
import { identityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
import { identityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
||||||
import { identityTlsCertAuthDALFactory } from "@app/services/identity-tls-cert-auth/identity-tls-cert-auth-dal";
|
import { identityTlsCertAuthDALFactory } from "@app/services/identity-tls-cert-auth/identity-tls-cert-auth-dal";
|
||||||
import { identityTlsCertAuthServiceFactory } from "@app/services/identity-tls-cert-auth/identity-tls-cert-auth-service";
|
import { identityTlsCertAuthServiceFactory } from "@app/services/identity-tls-cert-auth/identity-tls-cert-auth-service";
|
||||||
@@ -231,6 +227,14 @@ import { internalKmsDALFactory } from "@app/services/kms/internal-kms-dal";
|
|||||||
import { kmskeyDALFactory } from "@app/services/kms/kms-key-dal";
|
import { kmskeyDALFactory } from "@app/services/kms/kms-key-dal";
|
||||||
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
import { kmsRootConfigDALFactory } from "@app/services/kms/kms-root-config-dal";
|
||||||
import { kmsServiceFactory } from "@app/services/kms/kms-service";
|
import { kmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
import { membershipDALFactory } from "@app/services/membership/membership-dal";
|
||||||
|
import { membershipRoleDALFactory } from "@app/services/membership/membership-role-dal";
|
||||||
|
import { membershipGroupDALFactory } from "@app/services/membership-group/membership-group-dal";
|
||||||
|
import { membershipGroupServiceFactory } from "@app/services/membership-group/membership-group-service";
|
||||||
|
import { membershipIdentityDALFactory } from "@app/services/membership-identity/membership-identity-dal";
|
||||||
|
import { membershipIdentityServiceFactory } from "@app/services/membership-identity/membership-identity-service";
|
||||||
|
import { membershipUserDALFactory } from "@app/services/membership-user/membership-user-dal";
|
||||||
|
import { membershipUserServiceFactory } from "@app/services/membership-user/membership-user-service";
|
||||||
import { microsoftTeamsIntegrationDALFactory } from "@app/services/microsoft-teams/microsoft-teams-integration-dal";
|
import { microsoftTeamsIntegrationDALFactory } from "@app/services/microsoft-teams/microsoft-teams-integration-dal";
|
||||||
import { microsoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service";
|
import { microsoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service";
|
||||||
import { projectMicrosoftTeamsConfigDALFactory } from "@app/services/microsoft-teams/project-microsoft-teams-config-dal";
|
import { projectMicrosoftTeamsConfigDALFactory } from "@app/services/microsoft-teams/project-microsoft-teams-config-dal";
|
||||||
@@ -242,8 +246,6 @@ import { offlineUsageReportServiceFactory } from "@app/services/offline-usage-re
|
|||||||
import { incidentContactDALFactory } from "@app/services/org/incident-contacts-dal";
|
import { incidentContactDALFactory } from "@app/services/org/incident-contacts-dal";
|
||||||
import { orgBotDALFactory } from "@app/services/org/org-bot-dal";
|
import { orgBotDALFactory } from "@app/services/org/org-bot-dal";
|
||||||
import { orgDALFactory } from "@app/services/org/org-dal";
|
import { orgDALFactory } from "@app/services/org/org-dal";
|
||||||
import { orgRoleDALFactory } from "@app/services/org/org-role-dal";
|
|
||||||
import { orgRoleServiceFactory } from "@app/services/org/org-role-service";
|
|
||||||
import { orgServiceFactory } from "@app/services/org/org-service";
|
import { orgServiceFactory } from "@app/services/org/org-service";
|
||||||
import { orgAdminServiceFactory } from "@app/services/org-admin/org-admin-service";
|
import { orgAdminServiceFactory } from "@app/services/org-admin/org-admin-service";
|
||||||
import { orgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
import { orgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
|
||||||
@@ -274,15 +276,14 @@ import { projectKeyDALFactory } from "@app/services/project-key/project-key-dal"
|
|||||||
import { projectKeyServiceFactory } from "@app/services/project-key/project-key-service";
|
import { projectKeyServiceFactory } from "@app/services/project-key/project-key-service";
|
||||||
import { projectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
import { projectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||||
import { projectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service";
|
import { projectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service";
|
||||||
import { projectUserMembershipRoleDALFactory } from "@app/services/project-membership/project-user-membership-role-dal";
|
|
||||||
import { projectRoleDALFactory } from "@app/services/project-role/project-role-dal";
|
|
||||||
import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service";
|
|
||||||
import { reminderDALFactory } from "@app/services/reminder/reminder-dal";
|
import { reminderDALFactory } from "@app/services/reminder/reminder-dal";
|
||||||
import { dailyReminderQueueServiceFactory } from "@app/services/reminder/reminder-queue";
|
import { dailyReminderQueueServiceFactory } from "@app/services/reminder/reminder-queue";
|
||||||
import { reminderServiceFactory } from "@app/services/reminder/reminder-service";
|
import { reminderServiceFactory } from "@app/services/reminder/reminder-service";
|
||||||
import { reminderRecipientDALFactory } from "@app/services/reminder-recipients/reminder-recipient-dal";
|
import { reminderRecipientDALFactory } from "@app/services/reminder-recipients/reminder-recipient-dal";
|
||||||
import { dailyResourceCleanUpQueueServiceFactory } from "@app/services/resource-cleanup/resource-cleanup-queue";
|
import { dailyResourceCleanUpQueueServiceFactory } from "@app/services/resource-cleanup/resource-cleanup-queue";
|
||||||
import { resourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
|
import { resourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
|
||||||
|
import { roleDALFactory } from "@app/services/role/role-dal";
|
||||||
|
import { roleServiceFactory } from "@app/services/role/role-service";
|
||||||
import { secretDALFactory } from "@app/services/secret/secret-dal";
|
import { secretDALFactory } from "@app/services/secret/secret-dal";
|
||||||
import { secretQueueFactory } from "@app/services/secret/secret-queue";
|
import { secretQueueFactory } from "@app/services/secret/secret-queue";
|
||||||
import { secretServiceFactory } from "@app/services/secret/secret-service";
|
import { secretServiceFactory } from "@app/services/secret/secret-service";
|
||||||
@@ -382,16 +383,12 @@ export const registerRoutes = async (
|
|||||||
const orgMembershipDAL = orgMembershipDALFactory(db);
|
const orgMembershipDAL = orgMembershipDALFactory(db);
|
||||||
const orgBotDAL = orgBotDALFactory(db);
|
const orgBotDAL = orgBotDALFactory(db);
|
||||||
const incidentContactDAL = incidentContactDALFactory(db);
|
const incidentContactDAL = incidentContactDALFactory(db);
|
||||||
const orgRoleDAL = orgRoleDALFactory(db);
|
|
||||||
const rateLimitDAL = rateLimitDALFactory(db);
|
const rateLimitDAL = rateLimitDALFactory(db);
|
||||||
const apiKeyDAL = apiKeyDALFactory(db);
|
const apiKeyDAL = apiKeyDALFactory(db);
|
||||||
|
|
||||||
const projectDAL = projectDALFactory(db);
|
const projectDAL = projectDALFactory(db);
|
||||||
const projectSshConfigDAL = projectSshConfigDALFactory(db);
|
const projectSshConfigDAL = projectSshConfigDALFactory(db);
|
||||||
const projectMembershipDAL = projectMembershipDALFactory(db);
|
const projectMembershipDAL = projectMembershipDALFactory(db);
|
||||||
const projectUserAdditionalPrivilegeDAL = projectUserAdditionalPrivilegeDALFactory(db);
|
|
||||||
const projectUserMembershipRoleDAL = projectUserMembershipRoleDALFactory(db);
|
|
||||||
const projectRoleDAL = projectRoleDALFactory(db);
|
|
||||||
const projectEnvDAL = projectEnvDALFactory(db);
|
const projectEnvDAL = projectEnvDALFactory(db);
|
||||||
const projectKeyDAL = projectKeyDALFactory(db);
|
const projectKeyDAL = projectKeyDALFactory(db);
|
||||||
const projectBotDAL = projectBotDALFactory(db);
|
const projectBotDAL = projectBotDALFactory(db);
|
||||||
@@ -423,8 +420,6 @@ export const registerRoutes = async (
|
|||||||
const identityAccessTokenDAL = identityAccessTokenDALFactory(db);
|
const identityAccessTokenDAL = identityAccessTokenDALFactory(db);
|
||||||
const identityOrgMembershipDAL = identityOrgDALFactory(db);
|
const identityOrgMembershipDAL = identityOrgDALFactory(db);
|
||||||
const identityProjectDAL = identityProjectDALFactory(db);
|
const identityProjectDAL = identityProjectDALFactory(db);
|
||||||
const identityProjectMembershipRoleDAL = identityProjectMembershipRoleDALFactory(db);
|
|
||||||
const identityProjectAdditionalPrivilegeDAL = identityProjectAdditionalPrivilegeDALFactory(db);
|
|
||||||
const identityAuthTemplateDAL = identityAuthTemplateDALFactory(db);
|
const identityAuthTemplateDAL = identityAuthTemplateDALFactory(db);
|
||||||
|
|
||||||
const identityTokenAuthDAL = identityTokenAuthDALFactory(db);
|
const identityTokenAuthDAL = identityTokenAuthDALFactory(db);
|
||||||
@@ -482,7 +477,6 @@ export const registerRoutes = async (
|
|||||||
const gitAppOrgDAL = gitAppDALFactory(db);
|
const gitAppOrgDAL = gitAppDALFactory(db);
|
||||||
const groupDAL = groupDALFactory(db);
|
const groupDAL = groupDALFactory(db);
|
||||||
const groupProjectDAL = groupProjectDALFactory(db);
|
const groupProjectDAL = groupProjectDALFactory(db);
|
||||||
const groupProjectMembershipRoleDAL = groupProjectMembershipRoleDALFactory(db);
|
|
||||||
const userGroupMembershipDAL = userGroupMembershipDALFactory(db);
|
const userGroupMembershipDAL = userGroupMembershipDALFactory(db);
|
||||||
const secretScanningDAL = secretScanningDALFactory(db);
|
const secretScanningDAL = secretScanningDALFactory(db);
|
||||||
const secretSharingDAL = secretSharingDALFactory(db);
|
const secretSharingDAL = secretSharingDALFactory(db);
|
||||||
@@ -531,17 +525,27 @@ export const registerRoutes = async (
|
|||||||
const secretScanningV2DAL = secretScanningV2DALFactory(db);
|
const secretScanningV2DAL = secretScanningV2DALFactory(db);
|
||||||
const keyValueStoreDAL = keyValueStoreDALFactory(db);
|
const keyValueStoreDAL = keyValueStoreDALFactory(db);
|
||||||
|
|
||||||
|
const membershipDAL = membershipDALFactory(db);
|
||||||
|
const membershipUserDAL = membershipUserDALFactory(db);
|
||||||
|
const membershipIdentityDAL = membershipIdentityDALFactory(db);
|
||||||
|
const membershipGroupDAL = membershipGroupDALFactory(db);
|
||||||
|
const additionalPrivilegeDAL = additionalPrivilegeDALFactory(db);
|
||||||
|
const membershipRoleDAL = membershipRoleDALFactory(db);
|
||||||
|
const roleDAL = roleDALFactory(db);
|
||||||
|
|
||||||
const eventBusService = eventBusFactory(server.redis);
|
const eventBusService = eventBusFactory(server.redis);
|
||||||
const sseService = sseServiceFactory(eventBusService, server.redis);
|
const sseService = sseServiceFactory(eventBusService, server.redis);
|
||||||
|
|
||||||
const permissionService = permissionServiceFactory({
|
const permissionService = permissionServiceFactory({
|
||||||
permissionDAL,
|
permissionDAL,
|
||||||
orgRoleDAL,
|
|
||||||
projectRoleDAL,
|
|
||||||
serviceTokenDAL,
|
serviceTokenDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
keyStore
|
keyStore,
|
||||||
|
roleDAL,
|
||||||
|
userDAL,
|
||||||
|
identityDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const assumePrivilegeService = assumePrivilegeServiceFactory({
|
const assumePrivilegeService = assumePrivilegeServiceFactory({
|
||||||
projectDAL,
|
projectDAL,
|
||||||
permissionService
|
permissionService
|
||||||
@@ -556,6 +560,57 @@ export const registerRoutes = async (
|
|||||||
projectDAL
|
projectDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL, membershipUserDAL });
|
||||||
|
|
||||||
|
const membershipUserService = membershipUserServiceFactory({
|
||||||
|
licenseService,
|
||||||
|
membershipRoleDAL,
|
||||||
|
membershipUserDAL,
|
||||||
|
orgDAL,
|
||||||
|
permissionService,
|
||||||
|
roleDAL,
|
||||||
|
userDAL,
|
||||||
|
projectDAL,
|
||||||
|
projectKeyDAL,
|
||||||
|
smtpService,
|
||||||
|
tokenService,
|
||||||
|
userAliasDAL,
|
||||||
|
userGroupMembershipDAL,
|
||||||
|
additionalPrivilegeDAL
|
||||||
|
});
|
||||||
|
|
||||||
|
const membershipIdentityService = membershipIdentityServiceFactory({
|
||||||
|
membershipIdentityDAL,
|
||||||
|
membershipRoleDAL,
|
||||||
|
orgDAL,
|
||||||
|
permissionService,
|
||||||
|
roleDAL,
|
||||||
|
additionalPrivilegeDAL
|
||||||
|
});
|
||||||
|
|
||||||
|
const membershipGroupService = membershipGroupServiceFactory({
|
||||||
|
membershipGroupDAL,
|
||||||
|
membershipRoleDAL,
|
||||||
|
roleDAL,
|
||||||
|
permissionService,
|
||||||
|
orgDAL
|
||||||
|
});
|
||||||
|
|
||||||
|
const roleService = roleServiceFactory({
|
||||||
|
permissionService,
|
||||||
|
roleDAL,
|
||||||
|
projectDAL,
|
||||||
|
identityDAL,
|
||||||
|
userDAL,
|
||||||
|
externalGroupOrgRoleMappingDAL
|
||||||
|
});
|
||||||
|
const additionalPrivilegeService = additionalPrivilegeServiceFactory({
|
||||||
|
additionalPrivilegeDAL,
|
||||||
|
membershipDAL,
|
||||||
|
orgDAL,
|
||||||
|
permissionService
|
||||||
|
});
|
||||||
|
|
||||||
const hsmService = hsmServiceFactory({
|
const hsmService = hsmServiceFactory({
|
||||||
hsmModule,
|
hsmModule,
|
||||||
envConfig
|
envConfig
|
||||||
@@ -621,31 +676,29 @@ export const registerRoutes = async (
|
|||||||
userDAL,
|
userDAL,
|
||||||
secretApprovalRequestDAL
|
secretApprovalRequestDAL
|
||||||
});
|
});
|
||||||
const tokenService = tokenServiceFactory({ tokenDAL: authTokenDAL, userDAL, orgMembershipDAL });
|
|
||||||
|
|
||||||
const samlService = samlConfigServiceFactory({
|
const samlService = samlConfigServiceFactory({
|
||||||
identityMetadataDAL,
|
identityMetadataDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
userDAL,
|
userDAL,
|
||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
samlConfigDAL,
|
samlConfigDAL,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupProjectDAL,
|
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
tokenService,
|
tokenService,
|
||||||
smtpService,
|
smtpService,
|
||||||
kmsService
|
kmsService,
|
||||||
|
membershipRoleDAL,
|
||||||
|
membershipGroupDAL
|
||||||
});
|
});
|
||||||
const groupService = groupServiceFactory({
|
const groupService = groupServiceFactory({
|
||||||
userDAL,
|
userDAL,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
groupProjectDAL,
|
|
||||||
orgDAL,
|
orgDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
@@ -653,17 +706,13 @@ export const registerRoutes = async (
|
|||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
oidcConfigDAL
|
oidcConfigDAL,
|
||||||
|
membershipGroupDAL,
|
||||||
|
membershipRoleDAL
|
||||||
});
|
});
|
||||||
const groupProjectService = groupProjectServiceFactory({
|
const groupProjectService = groupProjectServiceFactory({
|
||||||
groupDAL,
|
groupDAL,
|
||||||
groupProjectDAL,
|
|
||||||
groupProjectMembershipRoleDAL,
|
|
||||||
userGroupMembershipDAL,
|
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectKeyDAL,
|
|
||||||
projectBotDAL,
|
|
||||||
projectRoleDAL,
|
|
||||||
permissionService
|
permissionService
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -708,18 +757,18 @@ export const registerRoutes = async (
|
|||||||
userDAL,
|
userDAL,
|
||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectUserAdditionalPrivilegeDAL,
|
|
||||||
projectMembershipDAL,
|
|
||||||
groupDAL,
|
|
||||||
groupProjectDAL,
|
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
smtpService,
|
smtpService,
|
||||||
externalGroupOrgRoleMappingDAL
|
externalGroupOrgRoleMappingDAL,
|
||||||
|
groupDAL,
|
||||||
|
membershipGroupDAL,
|
||||||
|
membershipRoleDAL,
|
||||||
|
membershipUserDAL,
|
||||||
|
additionalPrivilegeDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const githubOrgSyncConfigService = githubOrgSyncServiceFactory({
|
const githubOrgSyncConfigService = githubOrgSyncServiceFactory({
|
||||||
@@ -729,16 +778,16 @@ export const registerRoutes = async (
|
|||||||
permissionService,
|
permissionService,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
orgMembershipDAL
|
orgMembershipDAL,
|
||||||
|
membershipRoleDAL,
|
||||||
|
membershipGroupDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const ldapService = ldapConfigServiceFactory({
|
const ldapService = ldapConfigServiceFactory({
|
||||||
ldapConfigDAL,
|
ldapConfigDAL,
|
||||||
ldapGroupMapDAL,
|
ldapGroupMapDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
groupDAL,
|
groupDAL,
|
||||||
groupProjectDAL,
|
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
@@ -749,7 +798,9 @@ export const registerRoutes = async (
|
|||||||
licenseService,
|
licenseService,
|
||||||
tokenService,
|
tokenService,
|
||||||
smtpService,
|
smtpService,
|
||||||
kmsService
|
kmsService,
|
||||||
|
membershipGroupDAL,
|
||||||
|
membershipRoleDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const telemetryService = telemetryServiceFactory({
|
const telemetryService = telemetryServiceFactory({
|
||||||
@@ -771,13 +822,12 @@ export const registerRoutes = async (
|
|||||||
const userService = userServiceFactory({
|
const userService = userServiceFactory({
|
||||||
userDAL,
|
userDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
tokenService,
|
tokenService,
|
||||||
permissionService,
|
permissionService,
|
||||||
groupProjectDAL,
|
groupProjectDAL,
|
||||||
smtpService,
|
smtpService,
|
||||||
projectMembershipDAL,
|
userAliasDAL,
|
||||||
userAliasDAL
|
membershipUserDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const upgradePathService = upgradePathServiceFactory({ keyStore });
|
const upgradePathService = upgradePathServiceFactory({ keyStore });
|
||||||
@@ -794,17 +844,18 @@ export const registerRoutes = async (
|
|||||||
tokenService,
|
tokenService,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
totpService,
|
totpService,
|
||||||
orgMembershipDAL,
|
|
||||||
auditLogService,
|
auditLogService,
|
||||||
notificationService
|
notificationService,
|
||||||
|
membershipRoleDAL,
|
||||||
|
membershipUserDAL
|
||||||
});
|
});
|
||||||
const passwordService = authPaswordServiceFactory({
|
const passwordService = authPaswordServiceFactory({
|
||||||
tokenService,
|
tokenService,
|
||||||
smtpService,
|
smtpService,
|
||||||
authDAL,
|
authDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
orgMembershipDAL,
|
totpConfigDAL,
|
||||||
totpConfigDAL
|
membershipUserDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectBotService = projectBotServiceFactory({ permissionService, projectBotDAL, projectDAL });
|
const projectBotService = projectBotServiceFactory({ permissionService, projectBotDAL, projectDAL });
|
||||||
@@ -826,14 +877,10 @@ export const registerRoutes = async (
|
|||||||
folderDAL,
|
folderDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
samlConfigDAL,
|
samlConfigDAL,
|
||||||
orgRoleDAL,
|
|
||||||
permissionService,
|
permissionService,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
incidentContactDAL,
|
incidentContactDAL,
|
||||||
tokenService,
|
tokenService,
|
||||||
projectUserAdditionalPrivilegeDAL,
|
|
||||||
projectUserMembershipRoleDAL,
|
|
||||||
projectRoleDAL,
|
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectMembershipDAL,
|
projectMembershipDAL,
|
||||||
orgMembershipDAL,
|
orgMembershipDAL,
|
||||||
@@ -846,7 +893,12 @@ export const registerRoutes = async (
|
|||||||
ldapConfigDAL,
|
ldapConfigDAL,
|
||||||
loginService,
|
loginService,
|
||||||
projectBotService,
|
projectBotService,
|
||||||
reminderService
|
reminderService,
|
||||||
|
membershipRoleDAL,
|
||||||
|
membershipUserDAL,
|
||||||
|
roleDAL,
|
||||||
|
userGroupMembershipDAL,
|
||||||
|
additionalPrivilegeDAL
|
||||||
});
|
});
|
||||||
const signupService = authSignupServiceFactory({
|
const signupService = authSignupServiceFactory({
|
||||||
tokenService,
|
tokenService,
|
||||||
@@ -857,18 +909,10 @@ export const registerRoutes = async (
|
|||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
groupProjectDAL,
|
|
||||||
projectMembershipDAL,
|
|
||||||
projectUserMembershipRoleDAL,
|
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgService,
|
orgService,
|
||||||
licenseService
|
licenseService,
|
||||||
});
|
membershipGroupDAL
|
||||||
const orgRoleService = orgRoleServiceFactory({
|
|
||||||
permissionService,
|
|
||||||
orgRoleDAL,
|
|
||||||
orgDAL,
|
|
||||||
externalGroupOrgRoleMappingDAL
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const microsoftTeamsService = microsoftTeamsServiceFactory({
|
const microsoftTeamsService = microsoftTeamsServiceFactory({
|
||||||
@@ -885,8 +929,6 @@ export const registerRoutes = async (
|
|||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
identityTokenAuthDAL,
|
identityTokenAuthDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
identityOrgMembershipDAL,
|
|
||||||
authService: loginService,
|
authService: loginService,
|
||||||
serverCfgDAL: superAdminDAL,
|
serverCfgDAL: superAdminDAL,
|
||||||
kmsRootConfigDAL,
|
kmsRootConfigDAL,
|
||||||
@@ -898,7 +940,10 @@ export const registerRoutes = async (
|
|||||||
microsoftTeamsService,
|
microsoftTeamsService,
|
||||||
invalidateCacheQueue,
|
invalidateCacheQueue,
|
||||||
smtpService,
|
smtpService,
|
||||||
tokenService
|
tokenService,
|
||||||
|
membershipIdentityDAL,
|
||||||
|
membershipRoleDAL,
|
||||||
|
membershipUserDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const offlineUsageReportService = offlineUsageReportServiceFactory({
|
const offlineUsageReportService = offlineUsageReportServiceFactory({
|
||||||
@@ -910,9 +955,10 @@ export const registerRoutes = async (
|
|||||||
smtpService,
|
smtpService,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectUserMembershipRoleDAL,
|
notificationService,
|
||||||
projectMembershipDAL,
|
membershipRoleDAL,
|
||||||
notificationService
|
membershipUserDAL,
|
||||||
|
projectMembershipDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const rateLimitService = rateLimitServiceFactory({
|
const rateLimitService = rateLimitServiceFactory({
|
||||||
@@ -938,32 +984,25 @@ export const registerRoutes = async (
|
|||||||
|
|
||||||
const projectMembershipService = projectMembershipServiceFactory({
|
const projectMembershipService = projectMembershipServiceFactory({
|
||||||
projectMembershipDAL,
|
projectMembershipDAL,
|
||||||
projectUserMembershipRoleDAL,
|
|
||||||
projectDAL,
|
projectDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectBotDAL,
|
|
||||||
orgDAL,
|
|
||||||
userDAL,
|
userDAL,
|
||||||
projectUserAdditionalPrivilegeDAL,
|
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
smtpService,
|
smtpService,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectRoleDAL,
|
|
||||||
groupProjectDAL,
|
groupProjectDAL,
|
||||||
secretReminderRecipientsDAL,
|
secretReminderRecipientsDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
notificationService
|
notificationService,
|
||||||
});
|
membershipUserDAL,
|
||||||
const projectUserAdditionalPrivilegeService = projectUserAdditionalPrivilegeServiceFactory({
|
additionalPrivilegeDAL,
|
||||||
permissionService,
|
membershipRoleDAL
|
||||||
projectMembershipDAL,
|
|
||||||
projectUserAdditionalPrivilegeDAL,
|
|
||||||
accessApprovalRequestDAL
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectKeyService = projectKeyServiceFactory({
|
const projectKeyService = projectKeyServiceFactory({
|
||||||
permissionService,
|
permissionService,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectMembershipDAL
|
membershipUserDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectQueueService = projectQueueFactory({
|
const projectQueueService = projectQueueFactory({
|
||||||
@@ -979,10 +1018,10 @@ export const registerRoutes = async (
|
|||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectBotDAL,
|
projectBotDAL,
|
||||||
projectMembershipDAL,
|
|
||||||
secretApprovalRequestDAL,
|
secretApprovalRequestDAL,
|
||||||
secretApprovalSecretDAL: secretApprovalRequestSecretDAL,
|
secretApprovalSecretDAL: secretApprovalRequestSecretDAL,
|
||||||
projectUserMembershipRoleDAL
|
membershipRoleDAL,
|
||||||
|
membershipUserDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const certificateAuthorityDAL = certificateAuthorityDALFactory(db);
|
const certificateAuthorityDAL = certificateAuthorityDALFactory(db);
|
||||||
@@ -1121,7 +1160,11 @@ export const registerRoutes = async (
|
|||||||
relayDAL,
|
relayDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
licenseService,
|
licenseService,
|
||||||
permissionService
|
permissionService,
|
||||||
|
orgDAL,
|
||||||
|
notificationService,
|
||||||
|
smtpService,
|
||||||
|
userDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const gatewayV2Service = gatewayV2ServiceFactory({
|
const gatewayV2Service = gatewayV2ServiceFactory({
|
||||||
@@ -1131,7 +1174,10 @@ export const registerRoutes = async (
|
|||||||
orgGatewayConfigV2DAL,
|
orgGatewayConfigV2DAL,
|
||||||
gatewayV2DAL,
|
gatewayV2DAL,
|
||||||
relayDAL,
|
relayDAL,
|
||||||
permissionService
|
permissionService,
|
||||||
|
orgDAL,
|
||||||
|
notificationService,
|
||||||
|
smtpService
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretSyncQueue = secretSyncQueueFactory({
|
const secretSyncQueue = secretSyncQueueFactory({
|
||||||
@@ -1194,14 +1240,15 @@ export const registerRoutes = async (
|
|||||||
snapshotSecretV2BridgeDAL,
|
snapshotSecretV2BridgeDAL,
|
||||||
secretApprovalRequestDAL,
|
secretApprovalRequestDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectUserMembershipRoleDAL,
|
|
||||||
orgService,
|
orgService,
|
||||||
resourceMetadataDAL,
|
resourceMetadataDAL,
|
||||||
folderCommitService,
|
folderCommitService,
|
||||||
secretSyncQueue,
|
secretSyncQueue,
|
||||||
reminderService,
|
reminderService,
|
||||||
eventBusService,
|
eventBusService,
|
||||||
licenseService
|
licenseService,
|
||||||
|
membershipRoleDAL,
|
||||||
|
membershipUserDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectService = projectServiceFactory({
|
const projectService = projectServiceFactory({
|
||||||
@@ -1212,13 +1259,10 @@ export const registerRoutes = async (
|
|||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
projectQueue: projectQueueService,
|
projectQueue: projectQueueService,
|
||||||
projectBotService,
|
projectBotService,
|
||||||
identityProjectDAL,
|
|
||||||
identityOrgMembershipDAL,
|
|
||||||
userDAL,
|
userDAL,
|
||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
orgDAL,
|
orgDAL,
|
||||||
projectMembershipDAL,
|
projectMembershipDAL,
|
||||||
projectRoleDAL,
|
|
||||||
folderDAL,
|
folderDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
pkiSubscriberDAL,
|
pkiSubscriberDAL,
|
||||||
@@ -1232,8 +1276,6 @@ export const registerRoutes = async (
|
|||||||
sshCertificateTemplateDAL,
|
sshCertificateTemplateDAL,
|
||||||
sshHostDAL,
|
sshHostDAL,
|
||||||
sshHostGroupDAL,
|
sshHostGroupDAL,
|
||||||
projectUserMembershipRoleDAL,
|
|
||||||
identityProjectMembershipRoleDAL,
|
|
||||||
keyStore,
|
keyStore,
|
||||||
kmsService,
|
kmsService,
|
||||||
certificateTemplateDAL,
|
certificateTemplateDAL,
|
||||||
@@ -1242,10 +1284,14 @@ export const registerRoutes = async (
|
|||||||
projectMicrosoftTeamsConfigDAL,
|
projectMicrosoftTeamsConfigDAL,
|
||||||
microsoftTeamsIntegrationDAL,
|
microsoftTeamsIntegrationDAL,
|
||||||
projectTemplateService,
|
projectTemplateService,
|
||||||
groupProjectDAL,
|
|
||||||
smtpService,
|
smtpService,
|
||||||
reminderService,
|
reminderService,
|
||||||
notificationService
|
notificationService,
|
||||||
|
membershipGroupDAL,
|
||||||
|
membershipIdentityDAL,
|
||||||
|
membershipRoleDAL,
|
||||||
|
membershipUserDAL,
|
||||||
|
roleDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectEnvService = projectEnvServiceFactory({
|
const projectEnvService = projectEnvServiceFactory({
|
||||||
@@ -1259,16 +1305,6 @@ export const registerRoutes = async (
|
|||||||
secretApprovalPolicyEnvironmentDAL: sapEnvironmentDAL
|
secretApprovalPolicyEnvironmentDAL: sapEnvironmentDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectRoleService = projectRoleServiceFactory({
|
|
||||||
permissionService,
|
|
||||||
projectRoleDAL,
|
|
||||||
projectUserMembershipRoleDAL,
|
|
||||||
identityProjectMembershipRoleDAL,
|
|
||||||
projectDAL,
|
|
||||||
identityDAL,
|
|
||||||
userDAL
|
|
||||||
});
|
|
||||||
|
|
||||||
const snapshotService = secretSnapshotServiceFactory({
|
const snapshotService = secretSnapshotServiceFactory({
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
@@ -1423,21 +1459,18 @@ export const registerRoutes = async (
|
|||||||
groupDAL,
|
groupDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
projectMembershipDAL,
|
|
||||||
projectDAL,
|
projectDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
accessApprovalRequestDAL,
|
accessApprovalRequestDAL,
|
||||||
additionalPrivilegeDAL: projectUserAdditionalPrivilegeDAL,
|
|
||||||
accessApprovalRequestReviewerDAL,
|
accessApprovalRequestReviewerDAL,
|
||||||
orgMembershipDAL
|
additionalPrivilegeDAL,
|
||||||
|
membershipUserDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const accessApprovalRequestService = accessApprovalRequestServiceFactory({
|
const accessApprovalRequestService = accessApprovalRequestServiceFactory({
|
||||||
projectDAL,
|
projectDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
accessApprovalRequestReviewerDAL,
|
accessApprovalRequestReviewerDAL,
|
||||||
additionalPrivilegeDAL: projectUserAdditionalPrivilegeDAL,
|
|
||||||
projectMembershipDAL,
|
|
||||||
accessApprovalPolicyDAL,
|
accessApprovalPolicyDAL,
|
||||||
accessApprovalRequestDAL,
|
accessApprovalRequestDAL,
|
||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
@@ -1449,7 +1482,8 @@ export const registerRoutes = async (
|
|||||||
groupDAL,
|
groupDAL,
|
||||||
microsoftTeamsService,
|
microsoftTeamsService,
|
||||||
projectMicrosoftTeamsConfigDAL,
|
projectMicrosoftTeamsConfigDAL,
|
||||||
notificationService
|
notificationService,
|
||||||
|
additionalPrivilegeDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretReplicationService = secretReplicationServiceFactory({
|
const secretReplicationService = secretReplicationServiceFactory({
|
||||||
@@ -1538,7 +1572,15 @@ export const registerRoutes = async (
|
|||||||
identityProjectDAL,
|
identityProjectDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
identityMetadataDAL,
|
identityMetadataDAL,
|
||||||
keyStore
|
keyStore,
|
||||||
|
orgDAL,
|
||||||
|
membershipIdentityDAL,
|
||||||
|
membershipRoleDAL
|
||||||
|
});
|
||||||
|
const identityProjectService = identityProjectServiceFactory({
|
||||||
|
identityProjectDAL,
|
||||||
|
membershipIdentityDAL,
|
||||||
|
permissionService
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityAuthTemplateService = identityAuthTemplateServiceFactory({
|
const identityAuthTemplateService = identityAuthTemplateServiceFactory({
|
||||||
@@ -1557,105 +1599,91 @@ export const registerRoutes = async (
|
|||||||
identityDAL
|
identityDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityProjectService = identityProjectServiceFactory({
|
|
||||||
permissionService,
|
|
||||||
projectDAL,
|
|
||||||
identityProjectDAL,
|
|
||||||
identityOrgMembershipDAL,
|
|
||||||
identityProjectMembershipRoleDAL,
|
|
||||||
projectRoleDAL
|
|
||||||
});
|
|
||||||
const identityProjectAdditionalPrivilegeService = identityProjectAdditionalPrivilegeServiceFactory({
|
|
||||||
projectDAL,
|
|
||||||
identityProjectAdditionalPrivilegeDAL,
|
|
||||||
permissionService,
|
|
||||||
identityProjectDAL
|
|
||||||
});
|
|
||||||
|
|
||||||
const identityProjectAdditionalPrivilegeV2Service = identityProjectAdditionalPrivilegeV2ServiceFactory({
|
|
||||||
projectDAL,
|
|
||||||
identityProjectAdditionalPrivilegeDAL,
|
|
||||||
permissionService,
|
|
||||||
identityProjectDAL
|
|
||||||
});
|
|
||||||
|
|
||||||
const identityTokenAuthService = identityTokenAuthServiceFactory({
|
const identityTokenAuthService = identityTokenAuthServiceFactory({
|
||||||
identityTokenAuthDAL,
|
identityTokenAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService
|
licenseService,
|
||||||
|
orgDAL,
|
||||||
|
membershipIdentityDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityUaService = identityUaServiceFactory({
|
const identityUaService = identityUaServiceFactory({
|
||||||
identityOrgMembershipDAL,
|
|
||||||
permissionService,
|
permissionService,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityUaClientSecretDAL,
|
identityUaClientSecretDAL,
|
||||||
identityUaDAL,
|
identityUaDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
keyStore
|
keyStore,
|
||||||
|
orgDAL,
|
||||||
|
membershipIdentityDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityKubernetesAuthService = identityKubernetesAuthServiceFactory({
|
const identityKubernetesAuthService = identityKubernetesAuthServiceFactory({
|
||||||
identityKubernetesAuthDAL,
|
identityKubernetesAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
gatewayService,
|
gatewayService,
|
||||||
|
orgDAL,
|
||||||
gatewayV2Service,
|
gatewayV2Service,
|
||||||
gatewayV2DAL,
|
gatewayV2DAL,
|
||||||
gatewayDAL,
|
gatewayDAL,
|
||||||
kmsService
|
kmsService,
|
||||||
|
membershipIdentityDAL
|
||||||
});
|
});
|
||||||
const identityGcpAuthService = identityGcpAuthServiceFactory({
|
const identityGcpAuthService = identityGcpAuthServiceFactory({
|
||||||
identityGcpAuthDAL,
|
identityGcpAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
orgDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService
|
licenseService,
|
||||||
|
membershipIdentityDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityAliCloudAuthService = identityAliCloudAuthServiceFactory({
|
const identityAliCloudAuthService = identityAliCloudAuthServiceFactory({
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
|
orgDAL,
|
||||||
identityAliCloudAuthDAL,
|
identityAliCloudAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
|
||||||
licenseService,
|
licenseService,
|
||||||
permissionService
|
permissionService,
|
||||||
|
membershipIdentityDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityTlsCertAuthService = identityTlsCertAuthServiceFactory({
|
const identityTlsCertAuthService = identityTlsCertAuthServiceFactory({
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityTlsCertAuthDAL,
|
identityTlsCertAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
|
||||||
licenseService,
|
licenseService,
|
||||||
permissionService,
|
permissionService,
|
||||||
kmsService
|
kmsService,
|
||||||
|
membershipIdentityDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityAwsAuthService = identityAwsAuthServiceFactory({
|
const identityAwsAuthService = identityAwsAuthServiceFactory({
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
|
orgDAL,
|
||||||
identityAwsAuthDAL,
|
identityAwsAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
|
||||||
licenseService,
|
licenseService,
|
||||||
permissionService
|
permissionService,
|
||||||
|
membershipIdentityDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityAzureAuthService = identityAzureAuthServiceFactory({
|
const identityAzureAuthService = identityAzureAuthServiceFactory({
|
||||||
identityAzureAuthDAL,
|
identityAzureAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
orgDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService
|
licenseService,
|
||||||
|
membershipIdentityDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityOciAuthService = identityOciAuthServiceFactory({
|
const identityOciAuthService = identityOciAuthServiceFactory({
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
|
orgDAL,
|
||||||
identityOciAuthDAL,
|
identityOciAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
|
||||||
licenseService,
|
licenseService,
|
||||||
permissionService
|
permissionService,
|
||||||
|
membershipIdentityDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const pitService = pitServiceFactory({
|
const pitService = pitServiceFactory({
|
||||||
@@ -1674,32 +1702,42 @@ export const registerRoutes = async (
|
|||||||
|
|
||||||
const identityOidcAuthService = identityOidcAuthServiceFactory({
|
const identityOidcAuthService = identityOidcAuthServiceFactory({
|
||||||
identityOidcAuthDAL,
|
identityOidcAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
orgDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
kmsService
|
kmsService,
|
||||||
|
membershipIdentityDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityJwtAuthService = identityJwtAuthServiceFactory({
|
const identityJwtAuthService = identityJwtAuthServiceFactory({
|
||||||
identityJwtAuthDAL,
|
identityJwtAuthDAL,
|
||||||
|
orgDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityOrgMembershipDAL,
|
|
||||||
licenseService,
|
licenseService,
|
||||||
kmsService
|
kmsService,
|
||||||
|
membershipIdentityDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const identityLdapAuthService = identityLdapAuthServiceFactory({
|
const identityLdapAuthService = identityLdapAuthServiceFactory({
|
||||||
identityLdapAuthDAL,
|
identityLdapAuthDAL,
|
||||||
|
orgDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
kmsService,
|
kmsService,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityOrgMembershipDAL,
|
|
||||||
licenseService,
|
licenseService,
|
||||||
identityDAL,
|
identityDAL,
|
||||||
identityAuthTemplateDAL,
|
identityAuthTemplateDAL,
|
||||||
keyStore
|
keyStore,
|
||||||
|
membershipIdentityDAL
|
||||||
|
});
|
||||||
|
|
||||||
|
const convertorService = convertorServiceFactory({
|
||||||
|
additionalPrivilegeDAL,
|
||||||
|
membershipDAL,
|
||||||
|
projectDAL,
|
||||||
|
groupDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const dynamicSecretProviders = buildDynamicSecretProviders({
|
const dynamicSecretProviders = buildDynamicSecretProviders({
|
||||||
@@ -1774,7 +1812,6 @@ export const registerRoutes = async (
|
|||||||
|
|
||||||
const oidcService = oidcConfigServiceFactory({
|
const oidcService = oidcConfigServiceFactory({
|
||||||
orgDAL,
|
orgDAL,
|
||||||
orgMembershipDAL,
|
|
||||||
userDAL,
|
userDAL,
|
||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
@@ -1787,9 +1824,10 @@ export const registerRoutes = async (
|
|||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
userGroupMembershipDAL,
|
userGroupMembershipDAL,
|
||||||
groupProjectDAL,
|
|
||||||
groupDAL,
|
groupDAL,
|
||||||
auditLogService
|
auditLogService,
|
||||||
|
membershipGroupDAL,
|
||||||
|
membershipRoleDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const userEngagementService = userEngagementServiceFactory({
|
const userEngagementService = userEngagementServiceFactory({
|
||||||
@@ -1845,8 +1883,8 @@ export const registerRoutes = async (
|
|||||||
const externalGroupOrgRoleMappingService = externalGroupOrgRoleMappingServiceFactory({
|
const externalGroupOrgRoleMappingService = externalGroupOrgRoleMappingServiceFactory({
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
orgRoleDAL,
|
externalGroupOrgRoleMappingDAL,
|
||||||
externalGroupOrgRoleMappingDAL
|
roleDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const appConnectionService = appConnectionServiceFactory({
|
const appConnectionService = appConnectionServiceFactory({
|
||||||
@@ -2192,7 +2230,6 @@ export const registerRoutes = async (
|
|||||||
groupProject: groupProjectService,
|
groupProject: groupProjectService,
|
||||||
permission: permissionService,
|
permission: permissionService,
|
||||||
org: orgService,
|
org: orgService,
|
||||||
orgRole: orgRoleService,
|
|
||||||
oidc: oidcService,
|
oidc: oidcService,
|
||||||
apiKey: apiKeyService,
|
apiKey: apiKeyService,
|
||||||
authToken: tokenService,
|
authToken: tokenService,
|
||||||
@@ -2202,7 +2239,6 @@ export const registerRoutes = async (
|
|||||||
projectMembership: projectMembershipService,
|
projectMembership: projectMembershipService,
|
||||||
projectKey: projectKeyService,
|
projectKey: projectKeyService,
|
||||||
projectEnv: projectEnvService,
|
projectEnv: projectEnvService,
|
||||||
projectRole: projectRoleService,
|
|
||||||
secret: secretService,
|
secret: secretService,
|
||||||
secretReplication: secretReplicationService,
|
secretReplication: secretReplicationService,
|
||||||
secretTag: secretTagService,
|
secretTag: secretTagService,
|
||||||
@@ -2217,7 +2253,6 @@ export const registerRoutes = async (
|
|||||||
identity: identityService,
|
identity: identityService,
|
||||||
identityAuthTemplate: identityAuthTemplateService,
|
identityAuthTemplate: identityAuthTemplateService,
|
||||||
identityAccessToken: identityAccessTokenService,
|
identityAccessToken: identityAccessTokenService,
|
||||||
identityProject: identityProjectService,
|
|
||||||
identityTokenAuth: identityTokenAuthService,
|
identityTokenAuth: identityTokenAuthService,
|
||||||
identityUa: identityUaService,
|
identityUa: identityUaService,
|
||||||
identityKubernetesAuth: identityKubernetesAuthService,
|
identityKubernetesAuth: identityKubernetesAuthService,
|
||||||
@@ -2264,9 +2299,6 @@ export const registerRoutes = async (
|
|||||||
scim: scimService,
|
scim: scimService,
|
||||||
secretBlindIndex: secretBlindIndexService,
|
secretBlindIndex: secretBlindIndexService,
|
||||||
telemetry: telemetryService,
|
telemetry: telemetryService,
|
||||||
projectUserAdditionalPrivilege: projectUserAdditionalPrivilegeService,
|
|
||||||
identityProjectAdditionalPrivilege: identityProjectAdditionalPrivilegeService,
|
|
||||||
identityProjectAdditionalPrivilegeV2: identityProjectAdditionalPrivilegeV2Service,
|
|
||||||
secretSharing: secretSharingService,
|
secretSharing: secretSharingService,
|
||||||
userEngagement: userEngagementService,
|
userEngagement: userEngagementService,
|
||||||
externalKms: externalKmsService,
|
externalKms: externalKmsService,
|
||||||
@@ -2300,7 +2332,15 @@ export const registerRoutes = async (
|
|||||||
pamResource: pamResourceService,
|
pamResource: pamResourceService,
|
||||||
pamAccount: pamAccountService,
|
pamAccount: pamAccountService,
|
||||||
pamSession: pamSessionService,
|
pamSession: pamSessionService,
|
||||||
upgradePath: upgradePathService
|
upgradePath: upgradePathService,
|
||||||
|
|
||||||
|
membershipUser: membershipUserService,
|
||||||
|
membershipIdentity: membershipIdentityService,
|
||||||
|
membershipGroup: membershipGroupService,
|
||||||
|
role: roleService,
|
||||||
|
additionalPrivilege: additionalPrivilegeService,
|
||||||
|
identityProject: identityProjectService,
|
||||||
|
convertor: convertorService
|
||||||
});
|
});
|
||||||
|
|
||||||
const cronJobs: CronJob[] = [];
|
const cronJobs: CronJob[] = [];
|
||||||
@@ -2330,6 +2370,16 @@ export const registerRoutes = async (
|
|||||||
cronJobs.push(configSyncJob);
|
cronJobs.push(configSyncJob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gatewayHealthcheckNotifyJob = await gatewayV2Service.initializeHealthcheckNotify();
|
||||||
|
if (gatewayHealthcheckNotifyJob) {
|
||||||
|
cronJobs.push(gatewayHealthcheckNotifyJob);
|
||||||
|
}
|
||||||
|
|
||||||
|
const relayHealthcheckNotifyJob = await relayService.initializeHealthcheckNotify();
|
||||||
|
if (relayHealthcheckNotifyJob) {
|
||||||
|
cronJobs.push(relayHealthcheckNotifyJob);
|
||||||
|
}
|
||||||
|
|
||||||
const oauthConfigSyncJob = await initializeOauthConfigSync();
|
const oauthConfigSyncJob = await initializeOauthConfigSync();
|
||||||
if (oauthConfigSyncJob) {
|
if (oauthConfigSyncJob) {
|
||||||
cronJobs.push(oauthConfigSyncJob);
|
cronJobs.push(oauthConfigSyncJob);
|
||||||
|
|||||||
@@ -209,11 +209,11 @@ export const SanitizedIdentityPrivilegeSchema = IdentityProjectAdditionalPrivile
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SanitizedRoleSchema = ProjectRolesSchema.extend({
|
export const SanitizedRoleSchema = ProjectRolesSchema.omit({ version: true }).extend({
|
||||||
permissions: UnpackedPermissionSchema.array()
|
permissions: UnpackedPermissionSchema.array()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SanitizedRoleSchemaV1 = ProjectRolesSchema.extend({
|
export const SanitizedRoleSchemaV1 = ProjectRolesSchema.omit({ version: true }).extend({
|
||||||
permissions: UnpackedPermissionSchema.array().transform((caslPermission) =>
|
permissions: UnpackedPermissionSchema.array().transform((caslPermission) =>
|
||||||
// first map and remove other actions of folder permission
|
// first map and remove other actions of folder permission
|
||||||
caslPermission
|
caslPermission
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
IdentitiesSchema,
|
IdentitiesSchema,
|
||||||
OrganizationsSchema,
|
OrganizationsSchema,
|
||||||
OrgMembershipsSchema,
|
OrgMembershipsSchema,
|
||||||
|
OrgMembershipStatus,
|
||||||
SuperAdminSchema,
|
SuperAdminSchema,
|
||||||
UsersSchema
|
UsersSchema
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
@@ -172,7 +173,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
email: true,
|
email: true,
|
||||||
id: true,
|
id: true,
|
||||||
superAdmin: true
|
superAdmin: true
|
||||||
}).array()
|
}).array(),
|
||||||
|
total: z.number()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -182,13 +184,11 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const users = await server.services.superAdmin.getUsers({
|
const result = await server.services.superAdmin.getUsers({
|
||||||
...req.query
|
...req.query
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return result;
|
||||||
users
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -230,7 +230,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
createdAt: z.date()
|
createdAt: z.date()
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
}).array()
|
}).array(),
|
||||||
|
total: z.number()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -240,13 +241,11 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const organizations = await server.services.superAdmin.getOrganizations({
|
const result = await server.services.superAdmin.getOrganizations({
|
||||||
...req.query
|
...req.query
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return result;
|
||||||
organizations
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -281,7 +280,10 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
organizationMembership
|
organizationMembership: {
|
||||||
|
...organizationMembership,
|
||||||
|
status: organizationMembership?.status || OrgMembershipStatus.Accepted
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -337,7 +339,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
.extend({
|
.extend({
|
||||||
isInstanceAdmin: z.boolean()
|
isInstanceAdmin: z.boolean()
|
||||||
})
|
})
|
||||||
.array()
|
.array(),
|
||||||
|
total: z.number()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -347,13 +350,11 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const identities = await server.services.superAdmin.getIdentities({
|
const result = await server.services.superAdmin.getIdentities({
|
||||||
...req.query
|
...req.query
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return result;
|
||||||
identities
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -895,7 +896,13 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const organizationMembership = await server.services.superAdmin.resendOrgInvite(req.params, req.permission);
|
const organizationMembership = await server.services.superAdmin.resendOrgInvite(req.params, req.permission);
|
||||||
return { organizationMembership };
|
|
||||||
|
return {
|
||||||
|
organizationMembership: {
|
||||||
|
...organizationMembership,
|
||||||
|
status: organizationMembership?.status || OrgMembershipStatus.Accepted
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -925,7 +932,12 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
req.params.organizationId,
|
req.params.organizationId,
|
||||||
req.permission
|
req.permission
|
||||||
);
|
);
|
||||||
return { organizationMembership };
|
return {
|
||||||
|
organizationMembership: {
|
||||||
|
...organizationMembership,
|
||||||
|
status: organizationMembership?.status || OrgMembershipStatus.Accepted
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
AccessScope,
|
||||||
|
OrgMembershipRole,
|
||||||
OrgMembershipsSchema,
|
OrgMembershipsSchema,
|
||||||
|
OrgMembershipStatus,
|
||||||
ProjectMembershipsSchema,
|
ProjectMembershipsSchema,
|
||||||
ProjectUserMembershipRolesSchema,
|
ProjectUserMembershipRolesSchema,
|
||||||
|
TemporaryPermissionMode,
|
||||||
UserEncryptionKeysSchema,
|
UserEncryptionKeysSchema,
|
||||||
UsersSchema
|
UsersSchema
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
@@ -13,7 +17,6 @@ import { ms } from "@app/lib/ms";
|
|||||||
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 { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
|
|
||||||
|
|
||||||
export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZodProvider) => {
|
export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
@@ -66,14 +69,23 @@ export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZ
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const memberships = await server.services.projectMembership.getProjectMemberships({
|
const { data: memberships } = await server.services.membershipUser.listMemberships({
|
||||||
actorId: req.permission.id,
|
permission: req.permission,
|
||||||
actor: req.permission.type,
|
scopeData: {
|
||||||
actorAuthMethod: req.permission.authMethod,
|
scope: AccessScope.Project,
|
||||||
actorOrgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
projectId: req.params.workspaceId
|
projectId: req.params.workspaceId
|
||||||
|
},
|
||||||
|
data: {}
|
||||||
});
|
});
|
||||||
return { memberships };
|
|
||||||
|
return {
|
||||||
|
memberships: memberships.map((el) => ({
|
||||||
|
...el,
|
||||||
|
userId: el.actorUserId as string,
|
||||||
|
projectId: req.params.workspaceId
|
||||||
|
}))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -124,15 +136,30 @@ export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZ
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const membership = await server.services.projectMembership.getProjectMembershipById({
|
const { userId } = await server.services.convertor.userMembershipIdToUserId(
|
||||||
actorId: req.permission.id,
|
req.params.membershipId,
|
||||||
actor: req.permission.type,
|
AccessScope.Project,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
req.permission.orgId
|
||||||
actorOrgId: req.permission.orgId,
|
);
|
||||||
projectId: req.params.workspaceId,
|
const membership = await server.services.membershipUser.getMembershipByUserId({
|
||||||
id: req.params.membershipId
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
projectId: req.params.workspaceId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
userId
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return { membership };
|
|
||||||
|
return {
|
||||||
|
membership: {
|
||||||
|
...membership,
|
||||||
|
userId,
|
||||||
|
projectId: req.params.workspaceId
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -241,14 +268,22 @@ export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZ
|
|||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.ADD_BATCH_PROJECT_MEMBER,
|
type: EventType.ADD_BATCH_PROJECT_MEMBER,
|
||||||
metadata: data.map(({ userId }) => ({
|
metadata: data.map(({ actorUserId }) => ({
|
||||||
userId: userId || "",
|
userId: actorUserId || "",
|
||||||
email: ""
|
email: ""
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { data, success: true };
|
return {
|
||||||
|
data: data.map((el) => ({
|
||||||
|
...el,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
role: OrgMembershipRole.Member,
|
||||||
|
status: el.status || OrgMembershipStatus.Accepted
|
||||||
|
})),
|
||||||
|
success: true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -282,7 +317,7 @@ export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZ
|
|||||||
z.object({
|
z.object({
|
||||||
role: z.string(),
|
role: z.string(),
|
||||||
isTemporary: z.literal(true),
|
isTemporary: z.literal(true),
|
||||||
temporaryMode: z.nativeEnum(ProjectUserMembershipTemporaryMode),
|
temporaryMode: z.nativeEnum(TemporaryPermissionMode),
|
||||||
temporaryRange: z.string().refine((val) => ms(val) > 0, "Temporary range must be a positive number"),
|
temporaryRange: z.string().refine((val) => ms(val) > 0, "Temporary range must be a positive number"),
|
||||||
temporaryAccessStartTime: z.string().datetime()
|
temporaryAccessStartTime: z.string().datetime()
|
||||||
})
|
})
|
||||||
@@ -300,30 +335,28 @@ export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZ
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const roles = await server.services.projectMembership.updateProjectMembership({
|
const { userId } = await server.services.convertor.userMembershipIdToUserId(
|
||||||
actorId: req.permission.id,
|
req.params.membershipId,
|
||||||
actor: req.permission.type,
|
AccessScope.Project,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
req.permission.orgId
|
||||||
actorOrgId: req.permission.orgId,
|
);
|
||||||
projectId: req.params.workspaceId,
|
|
||||||
membershipId: req.params.membershipId,
|
const { membership } = await server.services.membershipUser.updateMembership({
|
||||||
roles: req.body.roles
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
projectId: req.params.workspaceId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
userId
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
roles: req.body.roles
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// await server.services.auditLog.createAuditLog({
|
return { roles: membership.roles.map((el) => ({ ...el, projectMembershipId: req.params.membershipId })) };
|
||||||
// ...req.auditLogInfo,
|
|
||||||
// projectId: req.params.workspaceId,
|
|
||||||
// event: {
|
|
||||||
// type: EventType.UPDATE_USER_WORKSPACE_ROLE,
|
|
||||||
// metadata: {
|
|
||||||
// userId: membership.userId,
|
|
||||||
// newRole: req.body.role,
|
|
||||||
// oldRole: membership.role,
|
|
||||||
// email: ""
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
return { roles };
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -352,13 +385,22 @@ export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZ
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const membership = await server.services.projectMembership.deleteProjectMembership({
|
const { userId } = await server.services.convertor.userMembershipIdToUserId(
|
||||||
actorId: req.permission.id,
|
req.params.membershipId,
|
||||||
actor: req.permission.type,
|
AccessScope.Project,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
req.permission.orgId
|
||||||
actorOrgId: req.permission.orgId,
|
);
|
||||||
projectId: req.params.workspaceId,
|
|
||||||
membershipId: req.params.membershipId
|
const { membership } = await server.services.membershipUser.deleteMembership({
|
||||||
|
permission: req.permission,
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
projectId: req.params.workspaceId
|
||||||
|
},
|
||||||
|
selector: {
|
||||||
|
userId
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
@@ -367,12 +409,19 @@ export const registerDeprecatedProjectMembershipRouter = async (server: FastifyZ
|
|||||||
event: {
|
event: {
|
||||||
type: EventType.REMOVE_PROJECT_MEMBER,
|
type: EventType.REMOVE_PROJECT_MEMBER,
|
||||||
metadata: {
|
metadata: {
|
||||||
userId: membership.userId,
|
userId: membership.actorUserId as string,
|
||||||
email: ""
|
email: ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return { membership };
|
|
||||||
|
return {
|
||||||
|
membership: {
|
||||||
|
...membership,
|
||||||
|
userId,
|
||||||
|
projectId: req.params.workspaceId
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
AccessScope,
|
||||||
GroupProjectMembershipsSchema,
|
GroupProjectMembershipsSchema,
|
||||||
GroupsSchema,
|
GroupsSchema,
|
||||||
ProjectMembershipRole,
|
ProjectMembershipRole,
|
||||||
ProjectUserMembershipRolesSchema,
|
ProjectUserMembershipRolesSchema,
|
||||||
|
TemporaryPermissionMode,
|
||||||
UsersSchema
|
UsersSchema
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { EFilterReturnedUsers } from "@app/ee/services/group/group-types";
|
import { EFilterReturnedUsers } from "@app/ee/services/group/group-types";
|
||||||
import { ApiDocsTags, GROUPS, PROJECTS } from "@app/lib/api-docs";
|
import { ApiDocsTags, GROUPS, PROJECTS } from "@app/lib/api-docs";
|
||||||
import { ms } from "@app/lib/ms";
|
import { ms } from "@app/lib/ms";
|
||||||
|
import { isUuidV4 } from "@app/lib/validator";
|
||||||
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 { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
|
|
||||||
|
|
||||||
export const registerGroupProjectRouter = async (server: FastifyZodProvider) => {
|
export const registerGroupProjectRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
@@ -54,7 +56,7 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
z.object({
|
z.object({
|
||||||
role: z.string(),
|
role: z.string(),
|
||||||
isTemporary: z.literal(true),
|
isTemporary: z.literal(true),
|
||||||
temporaryMode: z.nativeEnum(ProjectUserMembershipTemporaryMode),
|
temporaryMode: z.nativeEnum(TemporaryPermissionMode),
|
||||||
temporaryRange: z.string().refine((val) => ms(val) > 0, "Temporary range must be a positive number"),
|
temporaryRange: z.string().refine((val) => ms(val) > 0, "Temporary range must be a positive number"),
|
||||||
temporaryAccessStartTime: z.string().datetime()
|
temporaryAccessStartTime: z.string().datetime()
|
||||||
})
|
})
|
||||||
@@ -73,17 +75,32 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const groupMembership = await server.services.groupProject.addGroupToProject({
|
let groupId = req.params.groupIdOrName;
|
||||||
actor: req.permission.type,
|
if (!isUuidV4(req.params.groupIdOrName)) {
|
||||||
actorId: req.permission.id,
|
const groupDetails = await server.services.convertor.getGroupIdFromName(groupId, req.permission.orgId);
|
||||||
actorAuthMethod: req.permission.authMethod,
|
groupId = groupDetails.groupId;
|
||||||
actorOrgId: req.permission.orgId,
|
}
|
||||||
roles: req.body.roles || [{ role: req.body.role }],
|
|
||||||
projectId: req.params.projectId,
|
const { membership: groupMembership } = await server.services.membershipGroup.createMembership({
|
||||||
groupIdOrName: req.params.groupIdOrName
|
permission: req.permission,
|
||||||
|
data: {
|
||||||
|
groupId,
|
||||||
|
roles: req.body.roles || [{ role: req.body.role, isTemporary: false }]
|
||||||
|
},
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
projectId: req.params.projectId
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { groupMembership };
|
return {
|
||||||
|
groupMembership: {
|
||||||
|
...groupMembership,
|
||||||
|
projectId: req.params.projectId,
|
||||||
|
groupId: groupMembership.actorGroupId as string
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -115,7 +132,7 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
z.object({
|
z.object({
|
||||||
role: z.string(),
|
role: z.string(),
|
||||||
isTemporary: z.literal(true),
|
isTemporary: z.literal(true),
|
||||||
temporaryMode: z.nativeEnum(ProjectUserMembershipTemporaryMode),
|
temporaryMode: z.nativeEnum(TemporaryPermissionMode),
|
||||||
temporaryRange: z.string().refine((val) => ms(val) > 0, "Temporary range must be a positive number"),
|
temporaryRange: z.string().refine((val) => ms(val) > 0, "Temporary range must be a positive number"),
|
||||||
temporaryAccessStartTime: z.string().datetime()
|
temporaryAccessStartTime: z.string().datetime()
|
||||||
})
|
})
|
||||||
@@ -131,17 +148,22 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const roles = await server.services.groupProject.updateGroupInProject({
|
const { membership: groupMembership } = await server.services.membershipGroup.updateMembership({
|
||||||
actor: req.permission.type,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
selector: {
|
||||||
actorAuthMethod: req.permission.authMethod,
|
groupId: req.params.groupId
|
||||||
actorOrgId: req.permission.orgId,
|
},
|
||||||
projectId: req.params.projectId,
|
data: {
|
||||||
groupId: req.params.groupId,
|
roles: req.body.roles
|
||||||
roles: req.body.roles
|
},
|
||||||
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
projectId: req.params.projectId
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { roles };
|
return { roles: groupMembership.roles.map((el) => ({ ...el, projectMembershipId: groupMembership.id })) };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -172,16 +194,25 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const groupMembership = await server.services.groupProject.removeGroupFromProject({
|
const { membership: groupMembership } = await server.services.membershipGroup.deleteMembership({
|
||||||
actor: req.permission.type,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
selector: {
|
||||||
actorAuthMethod: req.permission.authMethod,
|
groupId: req.params.groupId
|
||||||
actorOrgId: req.permission.orgId,
|
},
|
||||||
groupId: req.params.groupId,
|
scopeData: {
|
||||||
projectId: req.params.projectId
|
scope: AccessScope.Project,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
projectId: req.params.projectId
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { groupMembership };
|
return {
|
||||||
|
groupMembership: {
|
||||||
|
...groupMembership,
|
||||||
|
projectId: req.params.projectId,
|
||||||
|
groupId: groupMembership.actorGroupId as string
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -233,15 +264,17 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const groupMemberships = await server.services.groupProject.listGroupsInProject({
|
const { memberships: groupMemberships } = await server.services.membershipGroup.listMemberships({
|
||||||
actor: req.permission.type,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
data: {},
|
||||||
actorAuthMethod: req.permission.authMethod,
|
scopeData: {
|
||||||
actorOrgId: req.permission.orgId,
|
scope: AccessScope.Project,
|
||||||
projectId: req.params.projectId
|
orgId: req.permission.orgId,
|
||||||
|
projectId: req.params.projectId
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { groupMemberships };
|
return { groupMemberships: groupMemberships.map((el) => ({ ...el, groupId: el.actorGroupId as string })) };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -292,15 +325,25 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const groupMembership = await server.services.groupProject.getGroupInProject({
|
const { membership: groupMembership } = await server.services.membershipGroup.getMembershipByGroupId({
|
||||||
actor: req.permission.type,
|
permission: req.permission,
|
||||||
actorId: req.permission.id,
|
selector: {
|
||||||
actorAuthMethod: req.permission.authMethod,
|
groupId: req.params.groupId
|
||||||
actorOrgId: req.permission.orgId,
|
},
|
||||||
...req.params
|
scopeData: {
|
||||||
|
scope: AccessScope.Project,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
projectId: req.params.projectId
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { groupMembership };
|
return {
|
||||||
|
groupMembership: {
|
||||||
|
...groupMembership,
|
||||||
|
projectId: req.params.projectId,
|
||||||
|
groupId: groupMembership.actorGroupId as string
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export const registerIdentityAliCloudAuthRouter = async (server: FastifyZodProvi
|
|||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: identityMembershipOrg?.orgId,
|
orgId: identityMembershipOrg.scopeOrgId,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.LOGIN_IDENTITY_ALICLOUD_AUTH,
|
type: EventType.LOGIN_IDENTITY_ALICLOUD_AUTH,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export const registerIdentityAwsAuthRouter = async (server: FastifyZodProvider)
|
|||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: identityMembershipOrg?.orgId,
|
orgId: identityMembershipOrg.scopeOrgId,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.LOGIN_IDENTITY_AWS_AUTH,
|
type: EventType.LOGIN_IDENTITY_AWS_AUTH,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const registerIdentityAzureAuthRouter = async (server: FastifyZodProvider
|
|||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
orgId: identityMembershipOrg.orgId,
|
orgId: identityMembershipOrg.scopeOrgId,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.LOGIN_IDENTITY_AZURE_AUTH,
|
type: EventType.LOGIN_IDENTITY_AZURE_AUTH,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user