mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 01:28:33 +00:00
Merge branch 'main' into feat/point-in-time-revamp
This commit is contained in:
@@ -28,3 +28,15 @@ frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow
|
|||||||
docs/cli/commands/user.mdx:generic-api-key:51
|
docs/cli/commands/user.mdx:generic-api-key:51
|
||||||
frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretOverviewTableRow.tsx:generic-api-key:76
|
frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretOverviewTableRow.tsx:generic-api-key:76
|
||||||
docs/integrations/app-connections/hashicorp-vault.mdx:generic-api-key:188
|
docs/integrations/app-connections/hashicorp-vault.mdx:generic-api-key:188
|
||||||
|
cli/detect/config/gitleaks.toml:gcp-api-key:567
|
||||||
|
cli/detect/config/gitleaks.toml:gcp-api-key:569
|
||||||
|
cli/detect/config/gitleaks.toml:gcp-api-key:570
|
||||||
|
cli/detect/config/gitleaks.toml:gcp-api-key:572
|
||||||
|
cli/detect/config/gitleaks.toml:gcp-api-key:574
|
||||||
|
cli/detect/config/gitleaks.toml:gcp-api-key:575
|
||||||
|
cli/detect/config/gitleaks.toml:gcp-api-key:576
|
||||||
|
cli/detect/config/gitleaks.toml:gcp-api-key:577
|
||||||
|
cli/detect/config/gitleaks.toml:gcp-api-key:578
|
||||||
|
cli/detect/config/gitleaks.toml:gcp-api-key:579
|
||||||
|
cli/detect/config/gitleaks.toml:gcp-api-key:581
|
||||||
|
cli/detect/config/gitleaks.toml:gcp-api-key:582
|
||||||
|
|||||||
@@ -133,8 +133,8 @@ RUN apt-get update && apt-get install -y \
|
|||||||
RUN printf "[FreeTDS]\nDescription = FreeTDS Driver\nDriver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nSetup = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so\nFileUsage = 1\n" > /etc/odbcinst.ini
|
RUN printf "[FreeTDS]\nDescription = FreeTDS Driver\nDriver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nSetup = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so\nFileUsage = 1\n" > /etc/odbcinst.ini
|
||||||
|
|
||||||
# Install Infisical CLI
|
# Install Infisical CLI
|
||||||
RUN curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/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.31.1 \
|
&& apt-get update && apt-get install -y infisical=0.41.2 \
|
||||||
&& 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
|
||||||
@@ -171,6 +171,7 @@ ENV NODE_ENV production
|
|||||||
ENV STANDALONE_BUILD true
|
ENV STANDALONE_BUILD true
|
||||||
ENV STANDALONE_MODE true
|
ENV STANDALONE_MODE true
|
||||||
ENV ChrystokiConfigurationPath=/usr/safenet/lunaclient/
|
ENV ChrystokiConfigurationPath=/usr/safenet/lunaclient/
|
||||||
|
ENV NODE_OPTIONS="--max-old-space-size=1024"
|
||||||
|
|
||||||
WORKDIR /backend
|
WORKDIR /backend
|
||||||
|
|
||||||
|
|||||||
@@ -127,8 +127,8 @@ RUN apt-get update && apt-get install -y \
|
|||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Install Infisical CLI
|
# Install Infisical CLI
|
||||||
RUN curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/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.31.1 \
|
&& apt-get update && apt-get install -y infisical=0.41.2 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
@@ -168,6 +168,7 @@ ENV HTTPS_ENABLED false
|
|||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
ENV STANDALONE_BUILD true
|
ENV STANDALONE_BUILD true
|
||||||
ENV STANDALONE_MODE true
|
ENV STANDALONE_MODE true
|
||||||
|
ENV NODE_OPTIONS="--max-old-space-size=1024"
|
||||||
|
|
||||||
WORKDIR /backend
|
WORKDIR /backend
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -54,8 +54,8 @@ COPY --from=build /app .
|
|||||||
|
|
||||||
# Install Infisical CLI
|
# Install Infisical CLI
|
||||||
RUN apt-get install -y curl bash && \
|
RUN apt-get install -y curl bash && \
|
||||||
curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | bash && \
|
curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash && \
|
||||||
apt-get update && apt-get install -y infisical=0.8.1 git
|
apt-get update && apt-get install -y infisical=0.41.2 git
|
||||||
|
|
||||||
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \
|
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \
|
||||||
CMD node healthcheck.js
|
CMD node healthcheck.js
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ RUN mkdir -p /etc/softhsm2/tokens && \
|
|||||||
# ? App setup
|
# ? App setup
|
||||||
|
|
||||||
# Install Infisical CLI
|
# Install Infisical CLI
|
||||||
RUN curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | bash && \
|
RUN curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y infisical=0.8.1
|
apt-get install -y infisical=0.41.2
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
@@ -64,9 +64,9 @@ RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
|
|||||||
# ? App setup
|
# ? App setup
|
||||||
|
|
||||||
# Install Infisical CLI
|
# Install Infisical CLI
|
||||||
RUN curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | bash && \
|
RUN curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y infisical=0.8.1
|
apt-get install -y infisical=0.41.2
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
|
import RE2 from "re2";
|
||||||
|
|
||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
|
import { applyJitter } from "@app/lib/dates";
|
||||||
|
import { delay as delayMs } from "@app/lib/delay";
|
||||||
import { Lock } from "@app/lib/red-lock";
|
import { Lock } from "@app/lib/red-lock";
|
||||||
|
|
||||||
export const mockKeyStore = (): TKeyStoreFactory => {
|
export const mockKeyStore = (): TKeyStoreFactory => {
|
||||||
@@ -18,6 +22,27 @@ export const mockKeyStore = (): TKeyStoreFactory => {
|
|||||||
delete store[key];
|
delete store[key];
|
||||||
return 1;
|
return 1;
|
||||||
},
|
},
|
||||||
|
deleteItems: async ({ pattern, batchSize = 500, delay = 1500, jitter = 200 }) => {
|
||||||
|
const regex = new RE2(`^${pattern.replace(/[-[\]/{}()+?.\\^$|]/g, "\\$&").replace(/\*/g, ".*")}$`);
|
||||||
|
let totalDeleted = 0;
|
||||||
|
const keys = Object.keys(store);
|
||||||
|
|
||||||
|
for (let i = 0; i < keys.length; i += batchSize) {
|
||||||
|
const batch = keys.slice(i, i + batchSize);
|
||||||
|
|
||||||
|
for (const key of batch) {
|
||||||
|
if (regex.test(key)) {
|
||||||
|
delete store[key];
|
||||||
|
totalDeleted += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await delayMs(Math.max(0, applyJitter(delay, jitter)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalDeleted;
|
||||||
|
},
|
||||||
getItem: async (key) => {
|
getItem: async (key) => {
|
||||||
const value = store[key];
|
const value = store[key];
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
|
|||||||
Generated
+1989
-314
File diff suppressed because it is too large
Load Diff
@@ -38,8 +38,8 @@
|
|||||||
"build:frontend": "npm run build --prefix ../frontend",
|
"build:frontend": "npm run build --prefix ../frontend",
|
||||||
"start": "node --enable-source-maps dist/main.mjs",
|
"start": "node --enable-source-maps dist/main.mjs",
|
||||||
"type:check": "tsc --noEmit",
|
"type:check": "tsc --noEmit",
|
||||||
"lint:fix": "eslint --fix --ext js,ts ./src",
|
"lint:fix": "node --max-old-space-size=8192 ./node_modules/.bin/eslint --fix --ext js,ts ./src",
|
||||||
"lint": "eslint 'src/**/*.ts'",
|
"lint": "node --max-old-space-size=8192 ./node_modules/.bin/eslint 'src/**/*.ts'",
|
||||||
"test:unit": "vitest run -c vitest.unit.config.ts",
|
"test:unit": "vitest run -c vitest.unit.config.ts",
|
||||||
"test:e2e": "vitest run -c vitest.e2e.config.ts --bail=1",
|
"test:e2e": "vitest run -c vitest.e2e.config.ts --bail=1",
|
||||||
"test:e2e-watch": "vitest -c vitest.e2e.config.ts --bail=1",
|
"test:e2e-watch": "vitest -c vitest.e2e.config.ts --bail=1",
|
||||||
@@ -152,7 +152,8 @@
|
|||||||
"@infisical/quic": "^1.0.8",
|
"@infisical/quic": "^1.0.8",
|
||||||
"@node-saml/passport-saml": "^5.0.1",
|
"@node-saml/passport-saml": "^5.0.1",
|
||||||
"@octokit/auth-app": "^7.1.1",
|
"@octokit/auth-app": "^7.1.1",
|
||||||
"@octokit/plugin-paginate-graphql": "^5.2.4",
|
"@octokit/core": "^5.2.1",
|
||||||
|
"@octokit/plugin-paginate-graphql": "^4.0.1",
|
||||||
"@octokit/plugin-retry": "^5.0.5",
|
"@octokit/plugin-retry": "^5.0.5",
|
||||||
"@octokit/rest": "^20.0.2",
|
"@octokit/rest": "^20.0.2",
|
||||||
"@octokit/webhooks-types": "^7.3.1",
|
"@octokit/webhooks-types": "^7.3.1",
|
||||||
@@ -208,6 +209,7 @@
|
|||||||
"mysql2": "^3.9.8",
|
"mysql2": "^3.9.8",
|
||||||
"nanoid": "^3.3.8",
|
"nanoid": "^3.3.8",
|
||||||
"nodemailer": "^6.9.9",
|
"nodemailer": "^6.9.9",
|
||||||
|
"oci-sdk": "^2.108.0",
|
||||||
"odbc": "^2.4.9",
|
"odbc": "^2.4.9",
|
||||||
"openid-client": "^5.6.5",
|
"openid-client": "^5.6.5",
|
||||||
"ora": "^7.0.1",
|
"ora": "^7.0.1",
|
||||||
@@ -240,6 +242,6 @@
|
|||||||
"tweetnacl-util": "^0.15.1",
|
"tweetnacl-util": "^0.15.1",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"zod": "^3.22.4",
|
"zod": "^3.22.4",
|
||||||
"zod-to-json-schema": "^3.22.4"
|
"zod-to-json-schema": "^3.24.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+17
-1
@@ -67,6 +67,9 @@ import { TIdentityAzureAuthServiceFactory } from "@app/services/identity-azure-a
|
|||||||
import { TIdentityGcpAuthServiceFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-service";
|
import { TIdentityGcpAuthServiceFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-service";
|
||||||
import { TIdentityJwtAuthServiceFactory } from "@app/services/identity-jwt-auth/identity-jwt-auth-service";
|
import { TIdentityJwtAuthServiceFactory } from "@app/services/identity-jwt-auth/identity-jwt-auth-service";
|
||||||
import { TIdentityKubernetesAuthServiceFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-service";
|
import { TIdentityKubernetesAuthServiceFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-service";
|
||||||
|
import { TIdentityLdapAuthServiceFactory } from "@app/services/identity-ldap-auth/identity-ldap-auth-service";
|
||||||
|
import { TAllowedFields } from "@app/services/identity-ldap-auth/identity-ldap-auth-types";
|
||||||
|
import { TIdentityOciAuthServiceFactory } from "@app/services/identity-oci-auth/identity-oci-auth-service";
|
||||||
import { TIdentityOidcAuthServiceFactory } from "@app/services/identity-oidc-auth/identity-oidc-auth-service";
|
import { TIdentityOidcAuthServiceFactory } from "@app/services/identity-oidc-auth/identity-oidc-auth-service";
|
||||||
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
||||||
import { TIdentityTokenAuthServiceFactory } from "@app/services/identity-token-auth/identity-token-auth-service";
|
import { TIdentityTokenAuthServiceFactory } from "@app/services/identity-token-auth/identity-token-auth-service";
|
||||||
@@ -79,6 +82,7 @@ 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";
|
||||||
import { TPkiCollectionServiceFactory } from "@app/services/pki-collection/pki-collection-service";
|
import { TPkiCollectionServiceFactory } from "@app/services/pki-collection/pki-collection-service";
|
||||||
|
import { TPkiSubscriberServiceFactory } from "@app/services/pki-subscriber/pki-subscriber-service";
|
||||||
import { TProjectServiceFactory } from "@app/services/project/project-service";
|
import { TProjectServiceFactory } from "@app/services/project/project-service";
|
||||||
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
||||||
import { TProjectEnvServiceFactory } from "@app/services/project-env/project-env-service";
|
import { TProjectEnvServiceFactory } from "@app/services/project-env/project-env-service";
|
||||||
@@ -147,6 +151,13 @@ declare module "fastify" {
|
|||||||
providerAuthToken: string;
|
providerAuthToken: string;
|
||||||
externalProviderAccessToken?: string;
|
externalProviderAccessToken?: string;
|
||||||
};
|
};
|
||||||
|
passportMachineIdentity: {
|
||||||
|
identityId: string;
|
||||||
|
user: {
|
||||||
|
uid: string;
|
||||||
|
mail?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
kmipUser: {
|
kmipUser: {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
@@ -154,7 +165,9 @@ declare module "fastify" {
|
|||||||
};
|
};
|
||||||
auditLogInfo: Pick<TCreateAuditLogDTO, "userAgent" | "userAgentType" | "ipAddress" | "actor">;
|
auditLogInfo: Pick<TCreateAuditLogDTO, "userAgent" | "userAgentType" | "ipAddress" | "actor">;
|
||||||
ssoConfig: Awaited<ReturnType<TSamlConfigServiceFactory["getSaml"]>>;
|
ssoConfig: Awaited<ReturnType<TSamlConfigServiceFactory["getSaml"]>>;
|
||||||
ldapConfig: Awaited<ReturnType<TLdapConfigServiceFactory["getLdapCfg"]>>;
|
ldapConfig: Awaited<ReturnType<TLdapConfigServiceFactory["getLdapCfg"]>> & {
|
||||||
|
allowedFields?: TAllowedFields[];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FastifyInstance {
|
interface FastifyInstance {
|
||||||
@@ -198,8 +211,10 @@ declare module "fastify" {
|
|||||||
identityGcpAuth: TIdentityGcpAuthServiceFactory;
|
identityGcpAuth: TIdentityGcpAuthServiceFactory;
|
||||||
identityAwsAuth: TIdentityAwsAuthServiceFactory;
|
identityAwsAuth: TIdentityAwsAuthServiceFactory;
|
||||||
identityAzureAuth: TIdentityAzureAuthServiceFactory;
|
identityAzureAuth: TIdentityAzureAuthServiceFactory;
|
||||||
|
identityOciAuth: TIdentityOciAuthServiceFactory;
|
||||||
identityOidcAuth: TIdentityOidcAuthServiceFactory;
|
identityOidcAuth: TIdentityOidcAuthServiceFactory;
|
||||||
identityJwtAuth: TIdentityJwtAuthServiceFactory;
|
identityJwtAuth: TIdentityJwtAuthServiceFactory;
|
||||||
|
identityLdapAuth: TIdentityLdapAuthServiceFactory;
|
||||||
accessApprovalPolicy: TAccessApprovalPolicyServiceFactory;
|
accessApprovalPolicy: TAccessApprovalPolicyServiceFactory;
|
||||||
accessApprovalRequest: TAccessApprovalRequestServiceFactory;
|
accessApprovalRequest: TAccessApprovalRequestServiceFactory;
|
||||||
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
|
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
|
||||||
@@ -221,6 +236,7 @@ declare module "fastify" {
|
|||||||
certificateAuthorityCrl: TCertificateAuthorityCrlServiceFactory;
|
certificateAuthorityCrl: TCertificateAuthorityCrlServiceFactory;
|
||||||
certificateEst: TCertificateEstServiceFactory;
|
certificateEst: TCertificateEstServiceFactory;
|
||||||
pkiCollection: TPkiCollectionServiceFactory;
|
pkiCollection: TPkiCollectionServiceFactory;
|
||||||
|
pkiSubscriber: TPkiSubscriberServiceFactory;
|
||||||
secretScanning: TSecretScanningServiceFactory;
|
secretScanning: TSecretScanningServiceFactory;
|
||||||
license: TLicenseServiceFactory;
|
license: TLicenseServiceFactory;
|
||||||
trustedIp: TTrustedIpServiceFactory;
|
trustedIp: TTrustedIpServiceFactory;
|
||||||
|
|||||||
Vendored
+26
@@ -137,6 +137,9 @@ import {
|
|||||||
TIdentityMetadata,
|
TIdentityMetadata,
|
||||||
TIdentityMetadataInsert,
|
TIdentityMetadataInsert,
|
||||||
TIdentityMetadataUpdate,
|
TIdentityMetadataUpdate,
|
||||||
|
TIdentityOciAuths,
|
||||||
|
TIdentityOciAuthsInsert,
|
||||||
|
TIdentityOciAuthsUpdate,
|
||||||
TIdentityOidcAuths,
|
TIdentityOidcAuths,
|
||||||
TIdentityOidcAuthsInsert,
|
TIdentityOidcAuthsInsert,
|
||||||
TIdentityOidcAuthsUpdate,
|
TIdentityOidcAuthsUpdate,
|
||||||
@@ -227,6 +230,9 @@ import {
|
|||||||
TPkiCollections,
|
TPkiCollections,
|
||||||
TPkiCollectionsInsert,
|
TPkiCollectionsInsert,
|
||||||
TPkiCollectionsUpdate,
|
TPkiCollectionsUpdate,
|
||||||
|
TPkiSubscribers,
|
||||||
|
TPkiSubscribersInsert,
|
||||||
|
TPkiSubscribersUpdate,
|
||||||
TProjectBots,
|
TProjectBots,
|
||||||
TProjectBotsInsert,
|
TProjectBotsInsert,
|
||||||
TProjectBotsUpdate,
|
TProjectBotsUpdate,
|
||||||
@@ -450,6 +456,11 @@ import {
|
|||||||
TWorkflowIntegrationsInsert,
|
TWorkflowIntegrationsInsert,
|
||||||
TWorkflowIntegrationsUpdate
|
TWorkflowIntegrationsUpdate
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
|
import {
|
||||||
|
TIdentityLdapAuths,
|
||||||
|
TIdentityLdapAuthsInsert,
|
||||||
|
TIdentityLdapAuthsUpdate
|
||||||
|
} from "@app/db/schemas/identity-ldap-auths";
|
||||||
import {
|
import {
|
||||||
TMicrosoftTeamsIntegrations,
|
TMicrosoftTeamsIntegrations,
|
||||||
TMicrosoftTeamsIntegrationsInsert,
|
TMicrosoftTeamsIntegrationsInsert,
|
||||||
@@ -577,6 +588,11 @@ declare module "knex/types/tables" {
|
|||||||
TPkiCollectionItemsInsert,
|
TPkiCollectionItemsInsert,
|
||||||
TPkiCollectionItemsUpdate
|
TPkiCollectionItemsUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.PkiSubscriber]: KnexOriginal.CompositeTableType<
|
||||||
|
TPkiSubscribers,
|
||||||
|
TPkiSubscribersInsert,
|
||||||
|
TPkiSubscribersUpdate
|
||||||
|
>;
|
||||||
[TableName.UserGroupMembership]: KnexOriginal.CompositeTableType<
|
[TableName.UserGroupMembership]: KnexOriginal.CompositeTableType<
|
||||||
TUserGroupMembership,
|
TUserGroupMembership,
|
||||||
TUserGroupMembershipInsert,
|
TUserGroupMembershipInsert,
|
||||||
@@ -743,6 +759,11 @@ declare module "knex/types/tables" {
|
|||||||
TIdentityAzureAuthsInsert,
|
TIdentityAzureAuthsInsert,
|
||||||
TIdentityAzureAuthsUpdate
|
TIdentityAzureAuthsUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.IdentityOciAuth]: KnexOriginal.CompositeTableType<
|
||||||
|
TIdentityOciAuths,
|
||||||
|
TIdentityOciAuthsInsert,
|
||||||
|
TIdentityOciAuthsUpdate
|
||||||
|
>;
|
||||||
[TableName.IdentityOidcAuth]: KnexOriginal.CompositeTableType<
|
[TableName.IdentityOidcAuth]: KnexOriginal.CompositeTableType<
|
||||||
TIdentityOidcAuths,
|
TIdentityOidcAuths,
|
||||||
TIdentityOidcAuthsInsert,
|
TIdentityOidcAuthsInsert,
|
||||||
@@ -753,6 +774,11 @@ declare module "knex/types/tables" {
|
|||||||
TIdentityJwtAuthsInsert,
|
TIdentityJwtAuthsInsert,
|
||||||
TIdentityJwtAuthsUpdate
|
TIdentityJwtAuthsUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.IdentityLdapAuth]: KnexOriginal.CompositeTableType<
|
||||||
|
TIdentityLdapAuths,
|
||||||
|
TIdentityLdapAuthsInsert,
|
||||||
|
TIdentityLdapAuthsUpdate
|
||||||
|
>;
|
||||||
[TableName.IdentityUaClientSecret]: KnexOriginal.CompositeTableType<
|
[TableName.IdentityUaClientSecret]: KnexOriginal.CompositeTableType<
|
||||||
TIdentityUaClientSecrets,
|
TIdentityUaClientSecrets,
|
||||||
TIdentityUaClientSecretsInsert,
|
TIdentityUaClientSecretsInsert,
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.CertificateBody, "encryptedCertificateChain"))) {
|
||||||
|
await knex.schema.alterTable(TableName.CertificateBody, (t) => {
|
||||||
|
t.binary("encryptedCertificateChain").nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasTable(TableName.CertificateSecret))) {
|
||||||
|
await knex.schema.createTable(TableName.CertificateSecret, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
t.uuid("certId").notNullable().unique();
|
||||||
|
t.foreign("certId").references("id").inTable(TableName.Certificate).onDelete("CASCADE");
|
||||||
|
t.binary("encryptedPrivateKey").notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasTable(TableName.CertificateSecret)) {
|
||||||
|
await knex.schema.dropTable(TableName.CertificateSecret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.CertificateBody, "encryptedCertificateChain")) {
|
||||||
|
await knex.schema.alterTable(TableName.CertificateBody, (t) => {
|
||||||
|
t.dropColumn("encryptedCertificateChain");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.SshHostLoginUserMapping, "groupId"))) {
|
||||||
|
await knex.schema.alterTable(TableName.SshHostLoginUserMapping, (t) => {
|
||||||
|
t.uuid("groupId").nullable();
|
||||||
|
t.foreign("groupId").references("id").inTable(TableName.Groups).onDelete("CASCADE");
|
||||||
|
t.unique(["sshHostLoginUserId", "groupId"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.SshHostLoginUserMapping, "groupId")) {
|
||||||
|
await knex.schema.alterTable(TableName.SshHostLoginUserMapping, (t) => {
|
||||||
|
t.dropUnique(["sshHostLoginUserId", "groupId"]);
|
||||||
|
t.dropColumn("groupId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { ProjectType, TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.ProjectTemplates, "type"))) {
|
||||||
|
await knex.schema.alterTable(TableName.ProjectTemplates, (t) => {
|
||||||
|
// defaulting to sm for migration to set existing, new ones will always be specified on creation
|
||||||
|
t.string("type").defaultTo(ProjectType.SecretManager).notNullable();
|
||||||
|
t.jsonb("environments").nullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.ProjectTemplates, "type")) {
|
||||||
|
await knex.schema.alterTable(TableName.ProjectTemplates, (t) => {
|
||||||
|
t.dropColumn("type");
|
||||||
|
// not reverting nullable environments
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasTable(TableName.IdentityLdapAuth))) {
|
||||||
|
await knex.schema.createTable(TableName.IdentityLdapAuth, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
|
||||||
|
t.bigInteger("accessTokenTTL").defaultTo(7200).notNullable();
|
||||||
|
t.bigInteger("accessTokenMaxTTL").defaultTo(7200).notNullable();
|
||||||
|
t.bigInteger("accessTokenNumUsesLimit").defaultTo(0).notNullable();
|
||||||
|
t.jsonb("accessTokenTrustedIps").notNullable();
|
||||||
|
|
||||||
|
t.uuid("identityId").notNullable().unique();
|
||||||
|
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.binary("encryptedBindDN").notNullable();
|
||||||
|
t.binary("encryptedBindPass").notNullable();
|
||||||
|
t.binary("encryptedLdapCaCertificate").nullable();
|
||||||
|
|
||||||
|
t.string("url").notNullable();
|
||||||
|
t.string("searchBase").notNullable();
|
||||||
|
t.string("searchFilter").notNullable();
|
||||||
|
|
||||||
|
t.jsonb("allowedFields").nullable();
|
||||||
|
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.IdentityLdapAuth);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.IdentityLdapAuth);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.IdentityLdapAuth);
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasTable(TableName.PkiSubscriber))) {
|
||||||
|
await knex.schema.createTable(TableName.PkiSubscriber, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
t.string("projectId").notNullable();
|
||||||
|
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
||||||
|
t.uuid("caId").nullable();
|
||||||
|
t.foreign("caId").references("id").inTable(TableName.CertificateAuthority).onDelete("SET NULL");
|
||||||
|
t.string("name").notNullable();
|
||||||
|
t.string("commonName").notNullable();
|
||||||
|
t.specificType("subjectAlternativeNames", "text[]").notNullable();
|
||||||
|
t.string("ttl").notNullable();
|
||||||
|
t.specificType("keyUsages", "text[]").notNullable();
|
||||||
|
t.specificType("extendedKeyUsages", "text[]").notNullable();
|
||||||
|
t.string("status").notNullable(); // active / disabled
|
||||||
|
t.unique(["projectId", "name"]);
|
||||||
|
});
|
||||||
|
await createOnUpdateTrigger(knex, TableName.PkiSubscriber);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasSubscriberCol = await knex.schema.hasColumn(TableName.Certificate, "pkiSubscriberId");
|
||||||
|
if (!hasSubscriberCol) {
|
||||||
|
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||||
|
t.uuid("pkiSubscriberId").nullable();
|
||||||
|
t.foreign("pkiSubscriberId").references("id").inTable(TableName.PkiSubscriber).onDelete("SET NULL");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasSubscriberCol = await knex.schema.hasColumn(TableName.Certificate, "pkiSubscriberId");
|
||||||
|
if (hasSubscriberCol) {
|
||||||
|
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||||
|
t.dropColumn("pkiSubscriberId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await knex.schema.dropTableIfExists(TableName.PkiSubscriber);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.PkiSubscriber);
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasTable(TableName.IdentityOciAuth))) {
|
||||||
|
await knex.schema.createTable(TableName.IdentityOciAuth, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.bigInteger("accessTokenTTL").defaultTo(7200).notNullable();
|
||||||
|
t.bigInteger("accessTokenMaxTTL").defaultTo(7200).notNullable();
|
||||||
|
t.bigInteger("accessTokenNumUsesLimit").defaultTo(0).notNullable();
|
||||||
|
t.jsonb("accessTokenTrustedIps").notNullable();
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
t.uuid("identityId").notNullable().unique();
|
||||||
|
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
|
||||||
|
t.string("type").notNullable();
|
||||||
|
|
||||||
|
t.string("tenancyOcid").notNullable();
|
||||||
|
t.string("allowedUsernames").nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.IdentityOciAuth);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.IdentityOciAuth);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.IdentityOciAuth);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasGatewayIdColumn = await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "gatewayId");
|
||||||
|
|
||||||
|
if (!hasGatewayIdColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (table) => {
|
||||||
|
table.uuid("gatewayId").nullable();
|
||||||
|
table.foreign("gatewayId").references("id").inTable(TableName.Gateway).onDelete("SET NULL");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasGatewayIdColumn = await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "gatewayId");
|
||||||
|
|
||||||
|
if (hasGatewayIdColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (table) => {
|
||||||
|
table.dropForeign("gatewayId");
|
||||||
|
table.dropColumn("gatewayId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { inMemoryKeyStore } from "@app/keystore/memory";
|
||||||
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
|
import { initLogger } from "@app/lib/logger";
|
||||||
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
import { getMigrationEnvConfig } from "./utils/env-config";
|
||||||
|
import { getMigrationEncryptionServices } from "./utils/services";
|
||||||
|
|
||||||
|
// Note(daniel): We aren't dropping tables or columns in this migrations so we can easily rollback if needed.
|
||||||
|
// In the future we need to drop the projectGatewayId on the dynamic secrets table, and drop the project_gateways table entirely.
|
||||||
|
|
||||||
|
const BATCH_SIZE = 500;
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
knex.replicaNode = () => {
|
||||||
|
return knex;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.DynamicSecret, "gatewayId"))) {
|
||||||
|
await knex.schema.alterTable(TableName.DynamicSecret, (table) => {
|
||||||
|
table.uuid("gatewayId").nullable();
|
||||||
|
table.foreign("gatewayId").references("id").inTable(TableName.Gateway).onDelete("SET NULL");
|
||||||
|
|
||||||
|
table.index("gatewayId");
|
||||||
|
});
|
||||||
|
|
||||||
|
const existingDynamicSecretsWithProjectGatewayId = await knex(TableName.DynamicSecret)
|
||||||
|
.select(selectAllTableCols(TableName.DynamicSecret))
|
||||||
|
.whereNotNull(`${TableName.DynamicSecret}.projectGatewayId`)
|
||||||
|
.join(TableName.ProjectGateway, `${TableName.ProjectGateway}.id`, `${TableName.DynamicSecret}.projectGatewayId`)
|
||||||
|
.whereNotNull(`${TableName.ProjectGateway}.gatewayId`)
|
||||||
|
.select(
|
||||||
|
knex.ref("projectId").withSchema(TableName.ProjectGateway).as("projectId"),
|
||||||
|
knex.ref("gatewayId").withSchema(TableName.ProjectGateway).as("projectGatewayGatewayId")
|
||||||
|
);
|
||||||
|
|
||||||
|
initLogger();
|
||||||
|
const envConfig = getMigrationEnvConfig();
|
||||||
|
const keyStore = inMemoryKeyStore();
|
||||||
|
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||||
|
|
||||||
|
const updatedDynamicSecrets = await Promise.all(
|
||||||
|
existingDynamicSecretsWithProjectGatewayId.map(async (existingDynamicSecret) => {
|
||||||
|
if (!existingDynamicSecret.projectGatewayGatewayId) {
|
||||||
|
const result = {
|
||||||
|
...existingDynamicSecret,
|
||||||
|
gatewayId: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const { projectId, projectGatewayGatewayId, ...rest } = result;
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
|
type: KmsDataKey.SecretManager,
|
||||||
|
projectId: existingDynamicSecret.projectId
|
||||||
|
});
|
||||||
|
const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
|
type: KmsDataKey.SecretManager,
|
||||||
|
projectId: existingDynamicSecret.projectId
|
||||||
|
});
|
||||||
|
|
||||||
|
let decryptedStoredInput = JSON.parse(
|
||||||
|
secretManagerDecryptor({ cipherTextBlob: Buffer.from(existingDynamicSecret.encryptedInput) }).toString()
|
||||||
|
) as object;
|
||||||
|
|
||||||
|
// We're not removing the existing projectGatewayId from the input so we can easily rollback without having to re-encrypt the input
|
||||||
|
decryptedStoredInput = {
|
||||||
|
...decryptedStoredInput,
|
||||||
|
gatewayId: existingDynamicSecret.projectGatewayGatewayId
|
||||||
|
};
|
||||||
|
|
||||||
|
const encryptedInput = secretManagerEncryptor({
|
||||||
|
plainText: Buffer.from(JSON.stringify(decryptedStoredInput))
|
||||||
|
}).cipherTextBlob;
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
...existingDynamicSecret,
|
||||||
|
encryptedInput,
|
||||||
|
gatewayId: existingDynamicSecret.projectGatewayGatewayId
|
||||||
|
};
|
||||||
|
|
||||||
|
const { projectId, projectGatewayGatewayId, ...rest } = result;
|
||||||
|
return rest;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < updatedDynamicSecrets.length; i += BATCH_SIZE) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await knex(TableName.DynamicSecret)
|
||||||
|
.insert(updatedDynamicSecrets.slice(i, i + BATCH_SIZE))
|
||||||
|
.onConflict("id")
|
||||||
|
.merge();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
// no re-encryption needed as we keep the old projectGatewayId in the input
|
||||||
|
if (await knex.schema.hasColumn(TableName.DynamicSecret, "gatewayId")) {
|
||||||
|
await knex.schema.alterTable(TableName.DynamicSecret, (table) => {
|
||||||
|
table.dropForeign("gatewayId");
|
||||||
|
table.dropColumn("gatewayId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const columns = await knex.table(TableName.Organization).columnInfo();
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||||
|
if (!columns.secretsProductEnabled) {
|
||||||
|
t.boolean("secretsProductEnabled").defaultTo(true);
|
||||||
|
}
|
||||||
|
if (!columns.pkiProductEnabled) {
|
||||||
|
t.boolean("pkiProductEnabled").defaultTo(true);
|
||||||
|
}
|
||||||
|
if (!columns.kmsProductEnabled) {
|
||||||
|
t.boolean("kmsProductEnabled").defaultTo(true);
|
||||||
|
}
|
||||||
|
if (!columns.sshProductEnabled) {
|
||||||
|
t.boolean("sshProductEnabled").defaultTo(true);
|
||||||
|
}
|
||||||
|
if (!columns.scannerProductEnabled) {
|
||||||
|
t.boolean("scannerProductEnabled").defaultTo(true);
|
||||||
|
}
|
||||||
|
if (!columns.shareSecretsProductEnabled) {
|
||||||
|
t.boolean("shareSecretsProductEnabled").defaultTo(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const columns = await knex.table(TableName.Organization).columnInfo();
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||||
|
if (columns.secretsProductEnabled) {
|
||||||
|
t.dropColumn("secretsProductEnabled");
|
||||||
|
}
|
||||||
|
if (columns.pkiProductEnabled) {
|
||||||
|
t.dropColumn("pkiProductEnabled");
|
||||||
|
}
|
||||||
|
if (columns.kmsProductEnabled) {
|
||||||
|
t.dropColumn("kmsProductEnabled");
|
||||||
|
}
|
||||||
|
if (columns.sshProductEnabled) {
|
||||||
|
t.dropColumn("sshProductEnabled");
|
||||||
|
}
|
||||||
|
if (columns.scannerProductEnabled) {
|
||||||
|
t.dropColumn("scannerProductEnabled");
|
||||||
|
}
|
||||||
|
if (columns.shareSecretsProductEnabled) {
|
||||||
|
t.dropColumn("shareSecretsProductEnabled");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasSecretSharingColumn = await knex.schema.hasColumn(TableName.Project, "secretSharing");
|
||||||
|
if (!hasSecretSharingColumn) {
|
||||||
|
await knex.schema.table(TableName.Project, (table) => {
|
||||||
|
table.boolean("secretSharing").notNullable().defaultTo(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasSecretSharingColumn = await knex.schema.hasColumn(TableName.Project, "secretSharing");
|
||||||
|
if (hasSecretSharingColumn) {
|
||||||
|
await knex.schema.table(TableName.Project, (table) => {
|
||||||
|
table.dropColumn("secretSharing");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasLifetimeColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretLifetime");
|
||||||
|
const hasViewLimitColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretViewLimit");
|
||||||
|
|
||||||
|
if (!hasLifetimeColumn || !hasViewLimitColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||||
|
if (!hasLifetimeColumn) {
|
||||||
|
t.integer("maxSharedSecretLifetime").nullable().defaultTo(2592000); // 30 days in seconds
|
||||||
|
}
|
||||||
|
if (!hasViewLimitColumn) {
|
||||||
|
t.integer("maxSharedSecretViewLimit").nullable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasLifetimeColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretLifetime");
|
||||||
|
const hasViewLimitColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretViewLimit");
|
||||||
|
|
||||||
|
if (hasLifetimeColumn || hasViewLimitColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||||
|
if (hasLifetimeColumn) {
|
||||||
|
t.dropColumn("maxSharedSecretLifetime");
|
||||||
|
}
|
||||||
|
if (hasViewLimitColumn) {
|
||||||
|
t.dropColumn("maxSharedSecretViewLimit");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasTable(TableName.SecretSharing)) {
|
||||||
|
const hasEncryptedSalt = await knex.schema.hasColumn(TableName.SecretSharing, "encryptedSalt");
|
||||||
|
const hasAuthorizedEmails = await knex.schema.hasColumn(TableName.SecretSharing, "authorizedEmails");
|
||||||
|
|
||||||
|
if (!hasEncryptedSalt || !hasAuthorizedEmails) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
|
||||||
|
// These two columns are only needed when secrets are shared with a specific list of emails
|
||||||
|
|
||||||
|
if (!hasEncryptedSalt) {
|
||||||
|
t.binary("encryptedSalt").nullable();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasAuthorizedEmails) {
|
||||||
|
t.json("authorizedEmails").nullable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasTable(TableName.SecretSharing)) {
|
||||||
|
const hasEncryptedSalt = await knex.schema.hasColumn(TableName.SecretSharing, "encryptedSalt");
|
||||||
|
const hasAuthorizedEmails = await knex.schema.hasColumn(TableName.SecretSharing, "authorizedEmails");
|
||||||
|
|
||||||
|
if (hasEncryptedSalt || hasAuthorizedEmails) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
|
||||||
|
if (hasEncryptedSalt) {
|
||||||
|
t.dropColumn("encryptedSalt");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasAuthorizedEmails) {
|
||||||
|
t.dropColumn("authorizedEmails");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,8 @@ export const CertificateBodiesSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
certId: z.string().uuid(),
|
certId: z.string().uuid(),
|
||||||
encryptedCertificate: zodBuffer
|
encryptedCertificate: zodBuffer,
|
||||||
|
encryptedCertificateChain: zodBuffer.nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TCertificateBodies = z.infer<typeof CertificateBodiesSchema>;
|
export type TCertificateBodies = z.infer<typeof CertificateBodiesSchema>;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { zodBuffer } from "@app/lib/zod";
|
||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
export const CertificateSecretsSchema = z.object({
|
export const CertificateSecretsSchema = z.object({
|
||||||
@@ -12,8 +14,7 @@ export const CertificateSecretsSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
certId: z.string().uuid(),
|
certId: z.string().uuid(),
|
||||||
pk: z.string(),
|
encryptedPrivateKey: zodBuffer
|
||||||
sk: z.string()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TCertificateSecrets = z.infer<typeof CertificateSecretsSchema>;
|
export type TCertificateSecrets = z.infer<typeof CertificateSecretsSchema>;
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ export const CertificatesSchema = z.object({
|
|||||||
caCertId: z.string().uuid(),
|
caCertId: z.string().uuid(),
|
||||||
certificateTemplateId: z.string().uuid().nullable().optional(),
|
certificateTemplateId: z.string().uuid().nullable().optional(),
|
||||||
keyUsages: z.string().array().nullable().optional(),
|
keyUsages: z.string().array().nullable().optional(),
|
||||||
extendedKeyUsages: z.string().array().nullable().optional()
|
extendedKeyUsages: z.string().array().nullable().optional(),
|
||||||
|
pkiSubscriberId: z.string().uuid().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TCertificates = z.infer<typeof CertificatesSchema>;
|
export type TCertificates = z.infer<typeof CertificatesSchema>;
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ export const DynamicSecretsSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
encryptedInput: zodBuffer,
|
encryptedInput: zodBuffer,
|
||||||
projectGatewayId: z.string().uuid().nullable().optional()
|
projectGatewayId: z.string().uuid().nullable().optional(),
|
||||||
|
gatewayId: z.string().uuid().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TDynamicSecrets = z.infer<typeof DynamicSecretsSchema>;
|
export type TDynamicSecrets = z.infer<typeof DynamicSecretsSchema>;
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ export const IdentityKubernetesAuthsSchema = z.object({
|
|||||||
allowedNames: z.string(),
|
allowedNames: z.string(),
|
||||||
allowedAudience: z.string(),
|
allowedAudience: z.string(),
|
||||||
encryptedKubernetesTokenReviewerJwt: zodBuffer.nullable().optional(),
|
encryptedKubernetesTokenReviewerJwt: zodBuffer.nullable().optional(),
|
||||||
encryptedKubernetesCaCertificate: zodBuffer.nullable().optional()
|
encryptedKubernetesCaCertificate: zodBuffer.nullable().optional(),
|
||||||
|
gatewayId: z.string().uuid().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityKubernetesAuths = z.infer<typeof IdentityKubernetesAuthsSchema>;
|
export type TIdentityKubernetesAuths = z.infer<typeof IdentityKubernetesAuthsSchema>;
|
||||||
|
|||||||
@@ -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 { zodBuffer } from "@app/lib/zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const IdentityLdapAuthsSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
accessTokenTTL: z.coerce.number().default(7200),
|
||||||
|
accessTokenMaxTTL: z.coerce.number().default(7200),
|
||||||
|
accessTokenNumUsesLimit: z.coerce.number().default(0),
|
||||||
|
accessTokenTrustedIps: z.unknown(),
|
||||||
|
identityId: z.string().uuid(),
|
||||||
|
encryptedBindDN: zodBuffer,
|
||||||
|
encryptedBindPass: zodBuffer,
|
||||||
|
encryptedLdapCaCertificate: zodBuffer.nullable().optional(),
|
||||||
|
url: z.string(),
|
||||||
|
searchBase: z.string(),
|
||||||
|
searchFilter: z.string(),
|
||||||
|
allowedFields: z.unknown().nullable().optional(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TIdentityLdapAuths = z.infer<typeof IdentityLdapAuthsSchema>;
|
||||||
|
export type TIdentityLdapAuthsInsert = Omit<z.input<typeof IdentityLdapAuthsSchema>, TImmutableDBKeys>;
|
||||||
|
export type TIdentityLdapAuthsUpdate = Partial<Omit<z.input<typeof IdentityLdapAuthsSchema>, TImmutableDBKeys>>;
|
||||||
@@ -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 IdentityOciAuthsSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
accessTokenTTL: z.coerce.number().default(7200),
|
||||||
|
accessTokenMaxTTL: z.coerce.number().default(7200),
|
||||||
|
accessTokenNumUsesLimit: z.coerce.number().default(0),
|
||||||
|
accessTokenTrustedIps: z.unknown(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date(),
|
||||||
|
identityId: z.string().uuid(),
|
||||||
|
type: z.string(),
|
||||||
|
tenancyOcid: z.string(),
|
||||||
|
allowedUsernames: z.string().nullable().optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TIdentityOciAuths = z.infer<typeof IdentityOciAuthsSchema>;
|
||||||
|
export type TIdentityOciAuthsInsert = Omit<z.input<typeof IdentityOciAuthsSchema>, TImmutableDBKeys>;
|
||||||
|
export type TIdentityOciAuthsUpdate = Partial<Omit<z.input<typeof IdentityOciAuthsSchema>, TImmutableDBKeys>>;
|
||||||
@@ -43,6 +43,7 @@ export * from "./identity-gcp-auths";
|
|||||||
export * from "./identity-jwt-auths";
|
export * from "./identity-jwt-auths";
|
||||||
export * from "./identity-kubernetes-auths";
|
export * from "./identity-kubernetes-auths";
|
||||||
export * from "./identity-metadata";
|
export * from "./identity-metadata";
|
||||||
|
export * from "./identity-oci-auths";
|
||||||
export * from "./identity-oidc-auths";
|
export * from "./identity-oidc-auths";
|
||||||
export * from "./identity-org-memberships";
|
export * from "./identity-org-memberships";
|
||||||
export * from "./identity-project-additional-privilege";
|
export * from "./identity-project-additional-privilege";
|
||||||
@@ -75,6 +76,7 @@ export * from "./organizations";
|
|||||||
export * from "./pki-alerts";
|
export * from "./pki-alerts";
|
||||||
export * from "./pki-collection-items";
|
export * from "./pki-collection-items";
|
||||||
export * from "./pki-collections";
|
export * from "./pki-collections";
|
||||||
|
export * from "./pki-subscribers";
|
||||||
export * from "./project-bots";
|
export * from "./project-bots";
|
||||||
export * from "./project-environments";
|
export * from "./project-environments";
|
||||||
export * from "./project-gateways";
|
export * from "./project-gateways";
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export enum TableName {
|
|||||||
CertificateBody = "certificate_bodies",
|
CertificateBody = "certificate_bodies",
|
||||||
CertificateSecret = "certificate_secrets",
|
CertificateSecret = "certificate_secrets",
|
||||||
CertificateTemplate = "certificate_templates",
|
CertificateTemplate = "certificate_templates",
|
||||||
|
PkiSubscriber = "pki_subscribers",
|
||||||
PkiAlert = "pki_alerts",
|
PkiAlert = "pki_alerts",
|
||||||
PkiCollection = "pki_collections",
|
PkiCollection = "pki_collections",
|
||||||
PkiCollectionItem = "pki_collection_items",
|
PkiCollectionItem = "pki_collection_items",
|
||||||
@@ -78,8 +79,10 @@ export enum TableName {
|
|||||||
IdentityAzureAuth = "identity_azure_auths",
|
IdentityAzureAuth = "identity_azure_auths",
|
||||||
IdentityUaClientSecret = "identity_ua_client_secrets",
|
IdentityUaClientSecret = "identity_ua_client_secrets",
|
||||||
IdentityAwsAuth = "identity_aws_auths",
|
IdentityAwsAuth = "identity_aws_auths",
|
||||||
|
IdentityOciAuth = "identity_oci_auths",
|
||||||
IdentityOidcAuth = "identity_oidc_auths",
|
IdentityOidcAuth = "identity_oidc_auths",
|
||||||
IdentityJwtAuth = "identity_jwt_auths",
|
IdentityJwtAuth = "identity_jwt_auths",
|
||||||
|
IdentityLdapAuth = "identity_ldap_auths",
|
||||||
IdentityOrgMembership = "identity_org_memberships",
|
IdentityOrgMembership = "identity_org_memberships",
|
||||||
IdentityProjectMembership = "identity_project_memberships",
|
IdentityProjectMembership = "identity_project_memberships",
|
||||||
IdentityProjectMembershipRole = "identity_project_membership_role",
|
IdentityProjectMembershipRole = "identity_project_membership_role",
|
||||||
@@ -191,11 +194,16 @@ export enum OrgMembershipStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum ProjectMembershipRole {
|
export enum ProjectMembershipRole {
|
||||||
|
// general
|
||||||
Admin = "admin",
|
Admin = "admin",
|
||||||
Member = "member",
|
Member = "member",
|
||||||
Custom = "custom",
|
Custom = "custom",
|
||||||
Viewer = "viewer",
|
Viewer = "viewer",
|
||||||
NoAccess = "no-access"
|
NoAccess = "no-access",
|
||||||
|
// ssh
|
||||||
|
SshHostBootstrapper = "ssh-host-bootstrapper",
|
||||||
|
// kms
|
||||||
|
KmsCryptographicOperator = "cryptographic-operator"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SecretEncryptionAlgo {
|
export enum SecretEncryptionAlgo {
|
||||||
@@ -232,8 +240,10 @@ export enum IdentityAuthMethod {
|
|||||||
GCP_AUTH = "gcp-auth",
|
GCP_AUTH = "gcp-auth",
|
||||||
AWS_AUTH = "aws-auth",
|
AWS_AUTH = "aws-auth",
|
||||||
AZURE_AUTH = "azure-auth",
|
AZURE_AUTH = "azure-auth",
|
||||||
|
OCI_AUTH = "oci-auth",
|
||||||
OIDC_AUTH = "oidc-auth",
|
OIDC_AUTH = "oidc-auth",
|
||||||
JWT_AUTH = "jwt-auth"
|
JWT_AUTH = "jwt-auth",
|
||||||
|
LDAP_AUTH = "ldap-auth"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ProjectType {
|
export enum ProjectType {
|
||||||
|
|||||||
@@ -28,7 +28,15 @@ export const OrganizationsSchema = z.object({
|
|||||||
privilegeUpgradeInitiatedByUsername: z.string().nullable().optional(),
|
privilegeUpgradeInitiatedByUsername: z.string().nullable().optional(),
|
||||||
privilegeUpgradeInitiatedAt: z.date().nullable().optional(),
|
privilegeUpgradeInitiatedAt: z.date().nullable().optional(),
|
||||||
bypassOrgAuthEnabled: z.boolean().default(false),
|
bypassOrgAuthEnabled: z.boolean().default(false),
|
||||||
userTokenExpiration: z.string().nullable().optional()
|
userTokenExpiration: z.string().nullable().optional(),
|
||||||
|
secretsProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||||
|
pkiProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||||
|
kmsProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||||
|
sshProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||||
|
scannerProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||||
|
shareSecretsProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||||
|
maxSharedSecretLifetime: z.number().default(2592000).nullable().optional(),
|
||||||
|
maxSharedSecretViewLimit: z.number().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
|
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
|
||||||
|
|||||||
@@ -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 PkiSubscribersSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date(),
|
||||||
|
projectId: z.string(),
|
||||||
|
caId: z.string().uuid().nullable().optional(),
|
||||||
|
name: z.string(),
|
||||||
|
commonName: z.string(),
|
||||||
|
subjectAlternativeNames: z.string().array(),
|
||||||
|
ttl: z.string(),
|
||||||
|
keyUsages: z.string().array(),
|
||||||
|
extendedKeyUsages: z.string().array(),
|
||||||
|
status: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TPkiSubscribers = z.infer<typeof PkiSubscribersSchema>;
|
||||||
|
export type TPkiSubscribersInsert = Omit<z.input<typeof PkiSubscribersSchema>, TImmutableDBKeys>;
|
||||||
|
export type TPkiSubscribersUpdate = Partial<Omit<z.input<typeof PkiSubscribersSchema>, TImmutableDBKeys>>;
|
||||||
@@ -12,10 +12,11 @@ export const ProjectTemplatesSchema = z.object({
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
description: z.string().nullable().optional(),
|
description: z.string().nullable().optional(),
|
||||||
roles: z.unknown(),
|
roles: z.unknown(),
|
||||||
environments: z.unknown(),
|
environments: z.unknown().nullable().optional(),
|
||||||
orgId: z.string().uuid(),
|
orgId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date(),
|
||||||
|
type: z.string().default("secret-manager")
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TProjectTemplates = z.infer<typeof ProjectTemplatesSchema>;
|
export type TProjectTemplates = z.infer<typeof ProjectTemplatesSchema>;
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ export const ProjectsSchema = z.object({
|
|||||||
description: z.string().nullable().optional(),
|
description: z.string().nullable().optional(),
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
enforceCapitalization: z.boolean().default(false),
|
enforceCapitalization: z.boolean().default(false),
|
||||||
hasDeleteProtection: z.boolean().default(true).nullable().optional()
|
hasDeleteProtection: z.boolean().default(false).nullable().optional(),
|
||||||
|
secretSharing: z.boolean().default(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TProjects = z.infer<typeof ProjectsSchema>;
|
export type TProjects = z.infer<typeof ProjectsSchema>;
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ export const SecretSharingSchema = z.object({
|
|||||||
password: z.string().nullable().optional(),
|
password: z.string().nullable().optional(),
|
||||||
encryptedSecret: zodBuffer.nullable().optional(),
|
encryptedSecret: zodBuffer.nullable().optional(),
|
||||||
identifier: z.string().nullable().optional(),
|
identifier: z.string().nullable().optional(),
|
||||||
type: z.string().default("share")
|
type: z.string().default("share"),
|
||||||
|
encryptedSalt: zodBuffer.nullable().optional(),
|
||||||
|
authorizedEmails: z.unknown().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretSharing = z.infer<typeof SecretSharingSchema>;
|
export type TSecretSharing = z.infer<typeof SecretSharingSchema>;
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ export const SshHostLoginUserMappingsSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
sshHostLoginUserId: z.string().uuid(),
|
sshHostLoginUserId: z.string().uuid(),
|
||||||
userId: z.string().uuid().nullable().optional()
|
userId: z.string().uuid().nullable().optional(),
|
||||||
|
groupId: z.string().uuid().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSshHostLoginUserMappings = z.infer<typeof SshHostLoginUserMappingsSchema>;
|
export type TSshHostLoginUserMappings = z.infer<typeof SshHostLoginUserMappingsSchema>;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { AccessApprovalRequestsReviewersSchema, AccessApprovalRequestsSchema, UsersSchema } from "@app/db/schemas";
|
import { AccessApprovalRequestsReviewersSchema, AccessApprovalRequestsSchema, UsersSchema } from "@app/db/schemas";
|
||||||
import { ApprovalStatus } from "@app/ee/services/access-approval-request/access-approval-request-types";
|
import { ApprovalStatus } from "@app/ee/services/access-approval-request/access-approval-request-types";
|
||||||
|
import { 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";
|
||||||
|
|
||||||
@@ -18,6 +19,9 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/",
|
url: "/",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
permissions: z.any().array(),
|
permissions: z.any().array(),
|
||||||
|
|||||||
@@ -121,14 +121,7 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
|
|||||||
identity: z.object({
|
identity: z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
id: z.string()
|
id: z.string()
|
||||||
}),
|
})
|
||||||
projects: z
|
|
||||||
.object({
|
|
||||||
name: z.string(),
|
|
||||||
id: z.string(),
|
|
||||||
slug: z.string()
|
|
||||||
})
|
|
||||||
.array()
|
|
||||||
}).array()
|
}).array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -158,17 +151,15 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
|
|||||||
identity: z.object({
|
identity: z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
id: z.string()
|
id: z.string()
|
||||||
}),
|
})
|
||||||
projectGatewayId: z.string()
|
|
||||||
}).array()
|
}).array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN, AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN, AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const gateways = await server.services.gateway.getProjectGateways({
|
const gateways = await server.services.gateway.listGateways({
|
||||||
projectId: req.params.projectId,
|
orgPermission: req.permission
|
||||||
projectPermission: req.permission
|
|
||||||
});
|
});
|
||||||
return { gateways };
|
return { gateways };
|
||||||
}
|
}
|
||||||
@@ -216,8 +207,7 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
|
|||||||
id: z.string()
|
id: z.string()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
name: slugSchema({ field: "name" }).optional(),
|
name: slugSchema({ field: "name" }).optional()
|
||||||
projectIds: z.string().array().optional()
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -230,8 +220,7 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
|
|||||||
const gateway = await server.services.gateway.updateGatewayById({
|
const gateway = await server.services.gateway.updateGatewayById({
|
||||||
orgPermission: req.permission,
|
orgPermission: req.permission,
|
||||||
id: req.params.id,
|
id: req.params.id,
|
||||||
name: req.body.name,
|
name: req.body.name
|
||||||
projectIds: req.body.projectIds
|
|
||||||
});
|
});
|
||||||
return { gateway };
|
return { gateway };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,9 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/login",
|
url: "/login",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
organizationSlug: z.string().trim()
|
organizationSlug: z.string().trim()
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ProjectMembershipRole, ProjectTemplatesSchema } from "@app/db/schemas";
|
import { ProjectMembershipRole, ProjectTemplatesSchema, ProjectType } 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 { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
import { ProjectTemplateDefaultEnvironments } from "@app/ee/services/project-template/project-template-constants";
|
|
||||||
import { isInfisicalProjectTemplate } from "@app/ee/services/project-template/project-template-fns";
|
import { isInfisicalProjectTemplate } from "@app/ee/services/project-template/project-template-fns";
|
||||||
import { ApiDocsTags, ProjectTemplates } from "@app/lib/api-docs";
|
import { ApiDocsTags, ProjectTemplates } from "@app/lib/api-docs";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
@@ -35,6 +34,7 @@ const SanitizedProjectTemplateSchema = ProjectTemplatesSchema.extend({
|
|||||||
position: z.number().min(1)
|
position: z.number().min(1)
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
|
.nullable()
|
||||||
});
|
});
|
||||||
|
|
||||||
const ProjectTemplateRolesSchema = z
|
const ProjectTemplateRolesSchema = z
|
||||||
@@ -104,6 +104,9 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider)
|
|||||||
hide: false,
|
hide: false,
|
||||||
tags: [ApiDocsTags.ProjectTemplates],
|
tags: [ApiDocsTags.ProjectTemplates],
|
||||||
description: "List project templates for the current organization.",
|
description: "List project templates for the current organization.",
|
||||||
|
querystring: z.object({
|
||||||
|
type: z.nativeEnum(ProjectType).optional().describe(ProjectTemplates.LIST.type)
|
||||||
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
projectTemplates: SanitizedProjectTemplateSchema.array()
|
projectTemplates: SanitizedProjectTemplateSchema.array()
|
||||||
@@ -112,7 +115,8 @@ export const registerProjectTemplateRouter = 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 projectTemplates = await server.services.projectTemplate.listProjectTemplatesByOrg(req.permission);
|
const { type } = req.query;
|
||||||
|
const projectTemplates = await server.services.projectTemplate.listProjectTemplatesByOrg(req.permission, type);
|
||||||
|
|
||||||
const auditTemplates = projectTemplates.filter((template) => !isInfisicalProjectTemplate(template.name));
|
const auditTemplates = projectTemplates.filter((template) => !isInfisicalProjectTemplate(template.name));
|
||||||
|
|
||||||
@@ -184,6 +188,7 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider)
|
|||||||
tags: [ApiDocsTags.ProjectTemplates],
|
tags: [ApiDocsTags.ProjectTemplates],
|
||||||
description: "Create a project template.",
|
description: "Create a project template.",
|
||||||
body: z.object({
|
body: z.object({
|
||||||
|
type: z.nativeEnum(ProjectType).describe(ProjectTemplates.CREATE.type),
|
||||||
name: slugSchema({ field: "name" })
|
name: slugSchema({ field: "name" })
|
||||||
.refine((val) => !isInfisicalProjectTemplate(val), {
|
.refine((val) => !isInfisicalProjectTemplate(val), {
|
||||||
message: `The requested project template name is reserved.`
|
message: `The requested project template name is reserved.`
|
||||||
@@ -191,9 +196,7 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider)
|
|||||||
.describe(ProjectTemplates.CREATE.name),
|
.describe(ProjectTemplates.CREATE.name),
|
||||||
description: z.string().max(256).trim().optional().describe(ProjectTemplates.CREATE.description),
|
description: z.string().max(256).trim().optional().describe(ProjectTemplates.CREATE.description),
|
||||||
roles: ProjectTemplateRolesSchema.default([]).describe(ProjectTemplates.CREATE.roles),
|
roles: ProjectTemplateRolesSchema.default([]).describe(ProjectTemplates.CREATE.roles),
|
||||||
environments: ProjectTemplateEnvironmentsSchema.default(ProjectTemplateDefaultEnvironments).describe(
|
environments: ProjectTemplateEnvironmentsSchema.describe(ProjectTemplates.CREATE.environments).optional()
|
||||||
ProjectTemplates.CREATE.environments
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
|||||||
@@ -166,6 +166,9 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/redirect/saml2/organizations/:orgSlug",
|
url: "/redirect/saml2/organizations/:orgSlug",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
orgSlug: z.string().trim()
|
orgSlug: z.string().trim()
|
||||||
@@ -192,6 +195,9 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/redirect/saml2/:samlConfigId",
|
url: "/redirect/saml2/:samlConfigId",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
samlConfigId: z.string().trim()
|
samlConfigId: z.string().trim()
|
||||||
@@ -218,6 +224,9 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/saml2/:samlConfigId",
|
url: "/saml2/:samlConfigId",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
samlConfigId: z.string().trim()
|
samlConfigId: z.string().trim()
|
||||||
|
|||||||
@@ -196,6 +196,9 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/Users",
|
url: "/Users",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
schemas: z.array(z.string()),
|
schemas: z.array(z.string()),
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { GitAppOrgSchema, SecretScanningGitRisksSchema } from "@app/db/schemas";
|
import { GitAppOrgSchema, SecretScanningGitRisksSchema } from "@app/db/schemas";
|
||||||
|
import { canUseSecretScanning } from "@app/ee/services/secret-scanning/secret-scanning-fns";
|
||||||
import {
|
import {
|
||||||
SecretScanningResolvedStatus,
|
SecretScanningResolvedStatus,
|
||||||
SecretScanningRiskStatus
|
SecretScanningRiskStatus
|
||||||
} from "@app/ee/services/secret-scanning/secret-scanning-types";
|
} from "@app/ee/services/secret-scanning/secret-scanning-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { OrderByDirection } from "@app/lib/types";
|
import { OrderByDirection } from "@app/lib/types";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
@@ -23,14 +23,14 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
|||||||
body: z.object({ organizationId: z.string().trim() }),
|
body: z.object({ organizationId: z.string().trim() }),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
sessionId: z.string()
|
sessionId: z.string(),
|
||||||
|
gitAppSlug: z.string()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const appCfg = getConfig();
|
if (!canUseSecretScanning(req.auth.orgId)) {
|
||||||
if (!appCfg.SECRET_SCANNING_ORG_WHITELIST?.includes(req.auth.orgId)) {
|
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Secret scanning is temporarily unavailable."
|
message: "Secret scanning is temporarily unavailable."
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export const registerSshCertificateTemplateRouter = async (server: FastifyZodPro
|
|||||||
allowCustomKeyIds: z.boolean().describe(SSH_CERTIFICATE_TEMPLATES.CREATE.allowCustomKeyIds)
|
allowCustomKeyIds: z.boolean().describe(SSH_CERTIFICATE_TEMPLATES.CREATE.allowCustomKeyIds)
|
||||||
})
|
})
|
||||||
.refine((data) => ms(data.maxTTL) >= ms(data.ttl), {
|
.refine((data) => ms(data.maxTTL) >= ms(data.ttl), {
|
||||||
message: "Max TLL must be greater than or equal to TTL",
|
message: "Max TTL must be greater than or equal to TTL",
|
||||||
path: ["maxTTL"]
|
path: ["maxTTL"]
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export const registerSshHostRouter = 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 host = await server.services.sshHost.getSshHost({
|
const host = await server.services.sshHost.getSshHostById({
|
||||||
sshHostId: req.params.sshHostId,
|
sshHostId: req.params.sshHostId,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ import { TProjectPermission } from "@app/lib/types";
|
|||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
import { TCreateAppConnectionDTO, TUpdateAppConnectionDTO } from "@app/services/app-connection/app-connection-types";
|
import { TCreateAppConnectionDTO, TUpdateAppConnectionDTO } from "@app/services/app-connection/app-connection-types";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-types";
|
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-types";
|
||||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||||
|
import { TAllowedFields } from "@app/services/identity-ldap-auth/identity-ldap-auth-types";
|
||||||
import { PkiItemType } from "@app/services/pki-collection/pki-collection-types";
|
import { PkiItemType } from "@app/services/pki-collection/pki-collection-types";
|
||||||
import { SecretSync, SecretSyncImportBehavior } from "@app/services/secret-sync/secret-sync-enums";
|
import { SecretSync, SecretSyncImportBehavior } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
import {
|
import {
|
||||||
@@ -119,44 +120,66 @@ export enum EventType {
|
|||||||
CREATE_TOKEN_IDENTITY_TOKEN_AUTH = "create-token-identity-token-auth",
|
CREATE_TOKEN_IDENTITY_TOKEN_AUTH = "create-token-identity-token-auth",
|
||||||
UPDATE_TOKEN_IDENTITY_TOKEN_AUTH = "update-token-identity-token-auth",
|
UPDATE_TOKEN_IDENTITY_TOKEN_AUTH = "update-token-identity-token-auth",
|
||||||
GET_TOKENS_IDENTITY_TOKEN_AUTH = "get-tokens-identity-token-auth",
|
GET_TOKENS_IDENTITY_TOKEN_AUTH = "get-tokens-identity-token-auth",
|
||||||
|
|
||||||
ADD_IDENTITY_TOKEN_AUTH = "add-identity-token-auth",
|
ADD_IDENTITY_TOKEN_AUTH = "add-identity-token-auth",
|
||||||
UPDATE_IDENTITY_TOKEN_AUTH = "update-identity-token-auth",
|
UPDATE_IDENTITY_TOKEN_AUTH = "update-identity-token-auth",
|
||||||
GET_IDENTITY_TOKEN_AUTH = "get-identity-token-auth",
|
GET_IDENTITY_TOKEN_AUTH = "get-identity-token-auth",
|
||||||
REVOKE_IDENTITY_TOKEN_AUTH = "revoke-identity-token-auth",
|
REVOKE_IDENTITY_TOKEN_AUTH = "revoke-identity-token-auth",
|
||||||
|
|
||||||
LOGIN_IDENTITY_KUBERNETES_AUTH = "login-identity-kubernetes-auth",
|
LOGIN_IDENTITY_KUBERNETES_AUTH = "login-identity-kubernetes-auth",
|
||||||
ADD_IDENTITY_KUBERNETES_AUTH = "add-identity-kubernetes-auth",
|
ADD_IDENTITY_KUBERNETES_AUTH = "add-identity-kubernetes-auth",
|
||||||
UPDATE_IDENTITY_KUBENETES_AUTH = "update-identity-kubernetes-auth",
|
UPDATE_IDENTITY_KUBENETES_AUTH = "update-identity-kubernetes-auth",
|
||||||
GET_IDENTITY_KUBERNETES_AUTH = "get-identity-kubernetes-auth",
|
GET_IDENTITY_KUBERNETES_AUTH = "get-identity-kubernetes-auth",
|
||||||
REVOKE_IDENTITY_KUBERNETES_AUTH = "revoke-identity-kubernetes-auth",
|
REVOKE_IDENTITY_KUBERNETES_AUTH = "revoke-identity-kubernetes-auth",
|
||||||
|
|
||||||
LOGIN_IDENTITY_OIDC_AUTH = "login-identity-oidc-auth",
|
LOGIN_IDENTITY_OIDC_AUTH = "login-identity-oidc-auth",
|
||||||
ADD_IDENTITY_OIDC_AUTH = "add-identity-oidc-auth",
|
ADD_IDENTITY_OIDC_AUTH = "add-identity-oidc-auth",
|
||||||
UPDATE_IDENTITY_OIDC_AUTH = "update-identity-oidc-auth",
|
UPDATE_IDENTITY_OIDC_AUTH = "update-identity-oidc-auth",
|
||||||
GET_IDENTITY_OIDC_AUTH = "get-identity-oidc-auth",
|
GET_IDENTITY_OIDC_AUTH = "get-identity-oidc-auth",
|
||||||
REVOKE_IDENTITY_OIDC_AUTH = "revoke-identity-oidc-auth",
|
REVOKE_IDENTITY_OIDC_AUTH = "revoke-identity-oidc-auth",
|
||||||
|
|
||||||
LOGIN_IDENTITY_JWT_AUTH = "login-identity-jwt-auth",
|
LOGIN_IDENTITY_JWT_AUTH = "login-identity-jwt-auth",
|
||||||
ADD_IDENTITY_JWT_AUTH = "add-identity-jwt-auth",
|
ADD_IDENTITY_JWT_AUTH = "add-identity-jwt-auth",
|
||||||
UPDATE_IDENTITY_JWT_AUTH = "update-identity-jwt-auth",
|
UPDATE_IDENTITY_JWT_AUTH = "update-identity-jwt-auth",
|
||||||
GET_IDENTITY_JWT_AUTH = "get-identity-jwt-auth",
|
GET_IDENTITY_JWT_AUTH = "get-identity-jwt-auth",
|
||||||
REVOKE_IDENTITY_JWT_AUTH = "revoke-identity-jwt-auth",
|
REVOKE_IDENTITY_JWT_AUTH = "revoke-identity-jwt-auth",
|
||||||
|
|
||||||
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
|
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
|
||||||
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
|
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
|
||||||
|
|
||||||
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
|
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
|
||||||
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET_BY_ID = "get-identity-universal-auth-client-secret-by-id",
|
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET_BY_ID = "get-identity-universal-auth-client-secret-by-id",
|
||||||
|
|
||||||
LOGIN_IDENTITY_GCP_AUTH = "login-identity-gcp-auth",
|
LOGIN_IDENTITY_GCP_AUTH = "login-identity-gcp-auth",
|
||||||
ADD_IDENTITY_GCP_AUTH = "add-identity-gcp-auth",
|
ADD_IDENTITY_GCP_AUTH = "add-identity-gcp-auth",
|
||||||
UPDATE_IDENTITY_GCP_AUTH = "update-identity-gcp-auth",
|
UPDATE_IDENTITY_GCP_AUTH = "update-identity-gcp-auth",
|
||||||
REVOKE_IDENTITY_GCP_AUTH = "revoke-identity-gcp-auth",
|
REVOKE_IDENTITY_GCP_AUTH = "revoke-identity-gcp-auth",
|
||||||
GET_IDENTITY_GCP_AUTH = "get-identity-gcp-auth",
|
GET_IDENTITY_GCP_AUTH = "get-identity-gcp-auth",
|
||||||
|
|
||||||
LOGIN_IDENTITY_AWS_AUTH = "login-identity-aws-auth",
|
LOGIN_IDENTITY_AWS_AUTH = "login-identity-aws-auth",
|
||||||
ADD_IDENTITY_AWS_AUTH = "add-identity-aws-auth",
|
ADD_IDENTITY_AWS_AUTH = "add-identity-aws-auth",
|
||||||
UPDATE_IDENTITY_AWS_AUTH = "update-identity-aws-auth",
|
UPDATE_IDENTITY_AWS_AUTH = "update-identity-aws-auth",
|
||||||
REVOKE_IDENTITY_AWS_AUTH = "revoke-identity-aws-auth",
|
REVOKE_IDENTITY_AWS_AUTH = "revoke-identity-aws-auth",
|
||||||
GET_IDENTITY_AWS_AUTH = "get-identity-aws-auth",
|
GET_IDENTITY_AWS_AUTH = "get-identity-aws-auth",
|
||||||
|
|
||||||
|
LOGIN_IDENTITY_OCI_AUTH = "login-identity-oci-auth",
|
||||||
|
ADD_IDENTITY_OCI_AUTH = "add-identity-oci-auth",
|
||||||
|
UPDATE_IDENTITY_OCI_AUTH = "update-identity-oci-auth",
|
||||||
|
REVOKE_IDENTITY_OCI_AUTH = "revoke-identity-oci-auth",
|
||||||
|
GET_IDENTITY_OCI_AUTH = "get-identity-oci-auth",
|
||||||
|
|
||||||
LOGIN_IDENTITY_AZURE_AUTH = "login-identity-azure-auth",
|
LOGIN_IDENTITY_AZURE_AUTH = "login-identity-azure-auth",
|
||||||
ADD_IDENTITY_AZURE_AUTH = "add-identity-azure-auth",
|
ADD_IDENTITY_AZURE_AUTH = "add-identity-azure-auth",
|
||||||
UPDATE_IDENTITY_AZURE_AUTH = "update-identity-azure-auth",
|
UPDATE_IDENTITY_AZURE_AUTH = "update-identity-azure-auth",
|
||||||
GET_IDENTITY_AZURE_AUTH = "get-identity-azure-auth",
|
GET_IDENTITY_AZURE_AUTH = "get-identity-azure-auth",
|
||||||
REVOKE_IDENTITY_AZURE_AUTH = "revoke-identity-azure-auth",
|
REVOKE_IDENTITY_AZURE_AUTH = "revoke-identity-azure-auth",
|
||||||
|
|
||||||
|
LOGIN_IDENTITY_LDAP_AUTH = "login-identity-ldap-auth",
|
||||||
|
ADD_IDENTITY_LDAP_AUTH = "add-identity-ldap-auth",
|
||||||
|
UPDATE_IDENTITY_LDAP_AUTH = "update-identity-ldap-auth",
|
||||||
|
GET_IDENTITY_LDAP_AUTH = "get-identity-ldap-auth",
|
||||||
|
REVOKE_IDENTITY_LDAP_AUTH = "revoke-identity-ldap-auth",
|
||||||
|
|
||||||
CREATE_ENVIRONMENT = "create-environment",
|
CREATE_ENVIRONMENT = "create-environment",
|
||||||
UPDATE_ENVIRONMENT = "update-environment",
|
UPDATE_ENVIRONMENT = "update-environment",
|
||||||
DELETE_ENVIRONMENT = "delete-environment",
|
DELETE_ENVIRONMENT = "delete-environment",
|
||||||
@@ -224,6 +247,8 @@ export enum EventType {
|
|||||||
DELETE_CERT = "delete-cert",
|
DELETE_CERT = "delete-cert",
|
||||||
REVOKE_CERT = "revoke-cert",
|
REVOKE_CERT = "revoke-cert",
|
||||||
GET_CERT_BODY = "get-cert-body",
|
GET_CERT_BODY = "get-cert-body",
|
||||||
|
GET_CERT_PRIVATE_KEY = "get-cert-private-key",
|
||||||
|
GET_CERT_BUNDLE = "get-cert-bundle",
|
||||||
CREATE_PKI_ALERT = "create-pki-alert",
|
CREATE_PKI_ALERT = "create-pki-alert",
|
||||||
GET_PKI_ALERT = "get-pki-alert",
|
GET_PKI_ALERT = "get-pki-alert",
|
||||||
UPDATE_PKI_ALERT = "update-pki-alert",
|
UPDATE_PKI_ALERT = "update-pki-alert",
|
||||||
@@ -235,6 +260,13 @@ export enum EventType {
|
|||||||
GET_PKI_COLLECTION_ITEMS = "get-pki-collection-items",
|
GET_PKI_COLLECTION_ITEMS = "get-pki-collection-items",
|
||||||
ADD_PKI_COLLECTION_ITEM = "add-pki-collection-item",
|
ADD_PKI_COLLECTION_ITEM = "add-pki-collection-item",
|
||||||
DELETE_PKI_COLLECTION_ITEM = "delete-pki-collection-item",
|
DELETE_PKI_COLLECTION_ITEM = "delete-pki-collection-item",
|
||||||
|
CREATE_PKI_SUBSCRIBER = "create-pki-subscriber",
|
||||||
|
UPDATE_PKI_SUBSCRIBER = "update-pki-subscriber",
|
||||||
|
DELETE_PKI_SUBSCRIBER = "delete-pki-subscriber",
|
||||||
|
GET_PKI_SUBSCRIBER = "get-pki-subscriber",
|
||||||
|
ISSUE_PKI_SUBSCRIBER_CERT = "issue-pki-subscriber-cert",
|
||||||
|
SIGN_PKI_SUBSCRIBER_CERT = "sign-pki-subscriber-cert",
|
||||||
|
LIST_PKI_SUBSCRIBER_CERTS = "list-pki-subscriber-certs",
|
||||||
CREATE_KMS = "create-kms",
|
CREATE_KMS = "create-kms",
|
||||||
UPDATE_KMS = "update-kms",
|
UPDATE_KMS = "update-kms",
|
||||||
DELETE_KMS = "delete-kms",
|
DELETE_KMS = "delete-kms",
|
||||||
@@ -991,6 +1023,55 @@ interface GetIdentityAwsAuthEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LoginIdentityOciAuthEvent {
|
||||||
|
type: EventType.LOGIN_IDENTITY_OCI_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
identityOciAuthId: string;
|
||||||
|
identityAccessTokenId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddIdentityOciAuthEvent {
|
||||||
|
type: EventType.ADD_IDENTITY_OCI_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
tenancyOcid: string;
|
||||||
|
allowedUsernames: string | null;
|
||||||
|
accessTokenTTL: number;
|
||||||
|
accessTokenMaxTTL: number;
|
||||||
|
accessTokenNumUsesLimit: number;
|
||||||
|
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeleteIdentityOciAuthEvent {
|
||||||
|
type: EventType.REVOKE_IDENTITY_OCI_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateIdentityOciAuthEvent {
|
||||||
|
type: EventType.UPDATE_IDENTITY_OCI_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
tenancyOcid?: string;
|
||||||
|
allowedUsernames: string | null;
|
||||||
|
accessTokenTTL?: number;
|
||||||
|
accessTokenMaxTTL?: number;
|
||||||
|
accessTokenNumUsesLimit?: number;
|
||||||
|
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetIdentityOciAuthEvent {
|
||||||
|
type: EventType.GET_IDENTITY_OCI_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface LoginIdentityAzureAuthEvent {
|
interface LoginIdentityAzureAuthEvent {
|
||||||
type: EventType.LOGIN_IDENTITY_AZURE_AUTH;
|
type: EventType.LOGIN_IDENTITY_AZURE_AUTH;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -1040,6 +1121,55 @@ interface GetIdentityAzureAuthEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LoginIdentityLdapAuthEvent {
|
||||||
|
type: EventType.LOGIN_IDENTITY_LDAP_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
ldapUsername: string;
|
||||||
|
ldapEmail?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddIdentityLdapAuthEvent {
|
||||||
|
type: EventType.ADD_IDENTITY_LDAP_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
accessTokenTTL?: number;
|
||||||
|
accessTokenMaxTTL?: number;
|
||||||
|
accessTokenNumUsesLimit?: number;
|
||||||
|
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
|
||||||
|
allowedFields?: TAllowedFields[];
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateIdentityLdapAuthEvent {
|
||||||
|
type: EventType.UPDATE_IDENTITY_LDAP_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
accessTokenTTL?: number;
|
||||||
|
accessTokenMaxTTL?: number;
|
||||||
|
accessTokenNumUsesLimit?: number;
|
||||||
|
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
|
||||||
|
allowedFields?: TAllowedFields[];
|
||||||
|
url?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetIdentityLdapAuthEvent {
|
||||||
|
type: EventType.GET_IDENTITY_LDAP_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RevokeIdentityLdapAuthEvent {
|
||||||
|
type: EventType.REVOKE_IDENTITY_LDAP_AUTH;
|
||||||
|
metadata: {
|
||||||
|
identityId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface LoginIdentityOidcAuthEvent {
|
interface LoginIdentityOidcAuthEvent {
|
||||||
type: EventType.LOGIN_IDENTITY_OIDC_AUTH;
|
type: EventType.LOGIN_IDENTITY_OIDC_AUTH;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -1798,6 +1928,24 @@ interface GetCertBody {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GetCertPrivateKey {
|
||||||
|
type: EventType.GET_CERT_PRIVATE_KEY;
|
||||||
|
metadata: {
|
||||||
|
certId: string;
|
||||||
|
cn: string;
|
||||||
|
serialNumber: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetCertBundle {
|
||||||
|
type: EventType.GET_CERT_BUNDLE;
|
||||||
|
metadata: {
|
||||||
|
certId: string;
|
||||||
|
cn: string;
|
||||||
|
serialNumber: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface CreatePkiAlert {
|
interface CreatePkiAlert {
|
||||||
type: EventType.CREATE_PKI_ALERT;
|
type: EventType.CREATE_PKI_ALERT;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -1887,6 +2035,77 @@ interface DeletePkiCollectionItem {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CreatePkiSubscriber {
|
||||||
|
type: EventType.CREATE_PKI_SUBSCRIBER;
|
||||||
|
metadata: {
|
||||||
|
pkiSubscriberId: string;
|
||||||
|
caId?: string;
|
||||||
|
name: string;
|
||||||
|
commonName: string;
|
||||||
|
ttl: string;
|
||||||
|
subjectAlternativeNames: string[];
|
||||||
|
keyUsages: CertKeyUsage[];
|
||||||
|
extendedKeyUsages: CertExtendedKeyUsage[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdatePkiSubscriber {
|
||||||
|
type: EventType.UPDATE_PKI_SUBSCRIBER;
|
||||||
|
metadata: {
|
||||||
|
pkiSubscriberId: string;
|
||||||
|
caId?: string;
|
||||||
|
name?: string;
|
||||||
|
commonName?: string;
|
||||||
|
ttl?: string;
|
||||||
|
subjectAlternativeNames?: string[];
|
||||||
|
keyUsages?: CertKeyUsage[];
|
||||||
|
extendedKeyUsages?: CertExtendedKeyUsage[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeletePkiSubscriber {
|
||||||
|
type: EventType.DELETE_PKI_SUBSCRIBER;
|
||||||
|
metadata: {
|
||||||
|
pkiSubscriberId: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetPkiSubscriber {
|
||||||
|
type: EventType.GET_PKI_SUBSCRIBER;
|
||||||
|
metadata: {
|
||||||
|
pkiSubscriberId: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IssuePkiSubscriberCert {
|
||||||
|
type: EventType.ISSUE_PKI_SUBSCRIBER_CERT;
|
||||||
|
metadata: {
|
||||||
|
subscriberId: string;
|
||||||
|
name: string;
|
||||||
|
serialNumber: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SignPkiSubscriberCert {
|
||||||
|
type: EventType.SIGN_PKI_SUBSCRIBER_CERT;
|
||||||
|
metadata: {
|
||||||
|
subscriberId: string;
|
||||||
|
name: string;
|
||||||
|
serialNumber: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ListPkiSubscriberCerts {
|
||||||
|
type: EventType.LIST_PKI_SUBSCRIBER_CERTS;
|
||||||
|
metadata: {
|
||||||
|
subscriberId: string;
|
||||||
|
name: string;
|
||||||
|
projectId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface CreateKmsEvent {
|
interface CreateKmsEvent {
|
||||||
type: EventType.CREATE_KMS;
|
type: EventType.CREATE_KMS;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -2823,6 +3042,11 @@ export type Event =
|
|||||||
| UpdateIdentityAwsAuthEvent
|
| UpdateIdentityAwsAuthEvent
|
||||||
| GetIdentityAwsAuthEvent
|
| GetIdentityAwsAuthEvent
|
||||||
| DeleteIdentityAwsAuthEvent
|
| DeleteIdentityAwsAuthEvent
|
||||||
|
| LoginIdentityOciAuthEvent
|
||||||
|
| AddIdentityOciAuthEvent
|
||||||
|
| UpdateIdentityOciAuthEvent
|
||||||
|
| GetIdentityOciAuthEvent
|
||||||
|
| DeleteIdentityOciAuthEvent
|
||||||
| LoginIdentityAzureAuthEvent
|
| LoginIdentityAzureAuthEvent
|
||||||
| AddIdentityAzureAuthEvent
|
| AddIdentityAzureAuthEvent
|
||||||
| DeleteIdentityAzureAuthEvent
|
| DeleteIdentityAzureAuthEvent
|
||||||
@@ -2838,6 +3062,11 @@ export type Event =
|
|||||||
| UpdateIdentityJwtAuthEvent
|
| UpdateIdentityJwtAuthEvent
|
||||||
| GetIdentityJwtAuthEvent
|
| GetIdentityJwtAuthEvent
|
||||||
| DeleteIdentityJwtAuthEvent
|
| DeleteIdentityJwtAuthEvent
|
||||||
|
| LoginIdentityLdapAuthEvent
|
||||||
|
| AddIdentityLdapAuthEvent
|
||||||
|
| UpdateIdentityLdapAuthEvent
|
||||||
|
| GetIdentityLdapAuthEvent
|
||||||
|
| RevokeIdentityLdapAuthEvent
|
||||||
| CreateEnvironmentEvent
|
| CreateEnvironmentEvent
|
||||||
| GetEnvironmentEvent
|
| GetEnvironmentEvent
|
||||||
| UpdateEnvironmentEvent
|
| UpdateEnvironmentEvent
|
||||||
@@ -2897,6 +3126,8 @@ export type Event =
|
|||||||
| DeleteCert
|
| DeleteCert
|
||||||
| RevokeCert
|
| RevokeCert
|
||||||
| GetCertBody
|
| GetCertBody
|
||||||
|
| GetCertPrivateKey
|
||||||
|
| GetCertBundle
|
||||||
| CreatePkiAlert
|
| CreatePkiAlert
|
||||||
| GetPkiAlert
|
| GetPkiAlert
|
||||||
| UpdatePkiAlert
|
| UpdatePkiAlert
|
||||||
@@ -2908,6 +3139,13 @@ export type Event =
|
|||||||
| GetPkiCollectionItems
|
| GetPkiCollectionItems
|
||||||
| AddPkiCollectionItem
|
| AddPkiCollectionItem
|
||||||
| DeletePkiCollectionItem
|
| DeletePkiCollectionItem
|
||||||
|
| CreatePkiSubscriber
|
||||||
|
| UpdatePkiSubscriber
|
||||||
|
| DeletePkiSubscriber
|
||||||
|
| GetPkiSubscriber
|
||||||
|
| IssuePkiSubscriberCert
|
||||||
|
| SignPkiSubscriberCert
|
||||||
|
| ListPkiSubscriberCerts
|
||||||
| CreateKmsEvent
|
| CreateKmsEvent
|
||||||
| UpdateKmsEvent
|
| UpdateKmsEvent
|
||||||
| DeleteKmsEvent
|
| DeleteKmsEvent
|
||||||
|
|||||||
@@ -24,8 +24,16 @@ export const verifyHostInputValidity = async (host: string, isGateway = false) =
|
|||||||
if (net.isIPv4(el)) {
|
if (net.isIPv4(el)) {
|
||||||
exclusiveIps.push(el);
|
exclusiveIps.push(el);
|
||||||
} else {
|
} else {
|
||||||
const resolvedIps = await dns.resolve4(el);
|
try {
|
||||||
exclusiveIps.push(...resolvedIps);
|
const resolvedIps = await dns.resolve4(el);
|
||||||
|
exclusiveIps.push(...resolvedIps);
|
||||||
|
} catch (error) {
|
||||||
|
// only try lookup if not found
|
||||||
|
if ((error as { code: string })?.code !== "ENOTFOUND") throw error;
|
||||||
|
|
||||||
|
const resolvedIps = (await dns.lookup(el, { all: true, family: 4 })).map(({ address }) => address);
|
||||||
|
exclusiveIps.push(...resolvedIps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,8 +46,16 @@ export const verifyHostInputValidity = async (host: string, isGateway = false) =
|
|||||||
if (normalizedHost === "localhost" || normalizedHost === "host.docker.internal") {
|
if (normalizedHost === "localhost" || normalizedHost === "host.docker.internal") {
|
||||||
throw new BadRequestError({ message: "Invalid db host" });
|
throw new BadRequestError({ message: "Invalid db host" });
|
||||||
}
|
}
|
||||||
const resolvedIps = await dns.resolve4(host);
|
try {
|
||||||
inputHostIps.push(...resolvedIps);
|
const resolvedIps = await dns.resolve4(host);
|
||||||
|
inputHostIps.push(...resolvedIps);
|
||||||
|
} catch (error) {
|
||||||
|
// only try lookup if not found
|
||||||
|
if ((error as { code: string })?.code !== "ENOTFOUND") throw error;
|
||||||
|
|
||||||
|
const resolvedIps = (await dns.lookup(host, { all: true, family: 4 })).map(({ address }) => address);
|
||||||
|
inputHostIps.push(...resolvedIps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isGateway && !(appCfg.DYNAMIC_SECRET_ALLOW_INTERNAL_IP || appCfg.ALLOW_INTERNAL_IP_CONNECTIONS)) {
|
if (!isGateway && !(appCfg.DYNAMIC_SECRET_ALLOW_INTERNAL_IP || appCfg.ALLOW_INTERNAL_IP_CONNECTIONS)) {
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-fold
|
|||||||
|
|
||||||
import { TDynamicSecretLeaseDALFactory } from "../dynamic-secret-lease/dynamic-secret-lease-dal";
|
import { TDynamicSecretLeaseDALFactory } from "../dynamic-secret-lease/dynamic-secret-lease-dal";
|
||||||
import { TDynamicSecretLeaseQueueServiceFactory } from "../dynamic-secret-lease/dynamic-secret-lease-queue";
|
import { TDynamicSecretLeaseQueueServiceFactory } from "../dynamic-secret-lease/dynamic-secret-lease-queue";
|
||||||
import { TProjectGatewayDALFactory } from "../gateway/project-gateway-dal";
|
import { TGatewayDALFactory } from "../gateway/gateway-dal";
|
||||||
|
import { OrgPermissionGatewayActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
import { TDynamicSecretDALFactory } from "./dynamic-secret-dal";
|
import { TDynamicSecretDALFactory } from "./dynamic-secret-dal";
|
||||||
import {
|
import {
|
||||||
DynamicSecretStatus,
|
DynamicSecretStatus,
|
||||||
@@ -44,9 +45,9 @@ type TDynamicSecretServiceFactoryDep = {
|
|||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath" | "findBySecretPathMultiEnv">;
|
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath" | "findBySecretPathMultiEnv">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
|
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
projectGatewayDAL: Pick<TProjectGatewayDALFactory, "findOne">;
|
gatewayDAL: Pick<TGatewayDALFactory, "findOne" | "find">;
|
||||||
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -62,7 +63,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
dynamicSecretQueueService,
|
dynamicSecretQueueService,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
projectGatewayDAL,
|
gatewayDAL,
|
||||||
resourceMetadataDAL
|
resourceMetadataDAL
|
||||||
}: TDynamicSecretServiceFactoryDep) => {
|
}: TDynamicSecretServiceFactoryDep) => {
|
||||||
const create = async ({
|
const create = async ({
|
||||||
@@ -117,15 +118,31 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
const inputs = await selectedProvider.validateProviderInputs(provider.inputs);
|
const inputs = await selectedProvider.validateProviderInputs(provider.inputs);
|
||||||
|
|
||||||
let selectedGatewayId: string | null = null;
|
let selectedGatewayId: string | null = null;
|
||||||
if (inputs && typeof inputs === "object" && "projectGatewayId" in inputs && inputs.projectGatewayId) {
|
if (inputs && typeof inputs === "object" && "gatewayId" in inputs && inputs.gatewayId) {
|
||||||
const projectGatewayId = inputs.projectGatewayId as string;
|
const gatewayId = inputs.gatewayId as string;
|
||||||
|
|
||||||
const projectGateway = await projectGatewayDAL.findOne({ id: projectGatewayId, projectId });
|
const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: actorOrgId });
|
||||||
if (!projectGateway)
|
|
||||||
|
if (!gateway) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Project gateway with ${projectGatewayId} not found`
|
message: `Gateway with ID ${gatewayId} not found`
|
||||||
});
|
});
|
||||||
selectedGatewayId = projectGateway.id;
|
}
|
||||||
|
|
||||||
|
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
gateway.orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(orgPermission).throwUnlessCan(
|
||||||
|
OrgPermissionGatewayActions.AttachGateways,
|
||||||
|
OrgPermissionSubjects.Gateway
|
||||||
|
);
|
||||||
|
|
||||||
|
selectedGatewayId = gateway.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isConnected = await selectedProvider.validateConnection(provider.inputs);
|
const isConnected = await selectedProvider.validateConnection(provider.inputs);
|
||||||
@@ -146,7 +163,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
defaultTTL,
|
defaultTTL,
|
||||||
folderId: folder.id,
|
folderId: folder.id,
|
||||||
name,
|
name,
|
||||||
projectGatewayId: selectedGatewayId
|
gatewayId: selectedGatewayId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -255,20 +272,30 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
const updatedInput = await selectedProvider.validateProviderInputs(newInput);
|
const updatedInput = await selectedProvider.validateProviderInputs(newInput);
|
||||||
|
|
||||||
let selectedGatewayId: string | null = null;
|
let selectedGatewayId: string | null = null;
|
||||||
if (
|
if (updatedInput && typeof updatedInput === "object" && "gatewayId" in updatedInput && updatedInput?.gatewayId) {
|
||||||
updatedInput &&
|
const gatewayId = updatedInput.gatewayId as string;
|
||||||
typeof updatedInput === "object" &&
|
|
||||||
"projectGatewayId" in updatedInput &&
|
|
||||||
updatedInput?.projectGatewayId
|
|
||||||
) {
|
|
||||||
const projectGatewayId = updatedInput.projectGatewayId as string;
|
|
||||||
|
|
||||||
const projectGateway = await projectGatewayDAL.findOne({ id: projectGatewayId, projectId });
|
const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: actorOrgId });
|
||||||
if (!projectGateway)
|
if (!gateway) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Project gateway with ${projectGatewayId} not found`
|
message: `Gateway with ID ${gatewayId} not found`
|
||||||
});
|
});
|
||||||
selectedGatewayId = projectGateway.id;
|
}
|
||||||
|
|
||||||
|
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
gateway.orgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(orgPermission).throwUnlessCan(
|
||||||
|
OrgPermissionGatewayActions.AttachGateways,
|
||||||
|
OrgPermissionSubjects.Gateway
|
||||||
|
);
|
||||||
|
|
||||||
|
selectedGatewayId = gateway.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isConnected = await selectedProvider.validateConnection(newInput);
|
const isConnected = await selectedProvider.validateConnection(newInput);
|
||||||
@@ -284,7 +311,7 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
defaultTTL,
|
defaultTTL,
|
||||||
name: newName ?? name,
|
name: newName ?? name,
|
||||||
status: null,
|
status: null,
|
||||||
projectGatewayId: selectedGatewayId
|
gatewayId: selectedGatewayId
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { SqlDatabaseProvider } from "./sql-database";
|
|||||||
import { TotpProvider } from "./totp";
|
import { TotpProvider } from "./totp";
|
||||||
|
|
||||||
type TBuildDynamicSecretProviderDTO = {
|
type TBuildDynamicSecretProviderDTO = {
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTls">;
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const buildDynamicSecretProviders = ({
|
export const buildDynamicSecretProviders = ({
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ export const DynamicSecretSqlDBSchema = z.object({
|
|||||||
revocationStatement: z.string().trim(),
|
revocationStatement: z.string().trim(),
|
||||||
renewStatement: z.string().trim().optional(),
|
renewStatement: z.string().trim().optional(),
|
||||||
ca: z.string().optional(),
|
ca: z.string().optional(),
|
||||||
projectGatewayId: z.string().nullable().optional()
|
gatewayId: z.string().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const DynamicSecretCassandraSchema = z.object({
|
export const DynamicSecretCassandraSchema = z.object({
|
||||||
|
|||||||
@@ -112,14 +112,14 @@ const generateUsername = (provider: SqlProviders) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type TSqlDatabaseProviderDTO = {
|
type TSqlDatabaseProviderDTO = {
|
||||||
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTls">;
|
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO): TDynamicProviderFns => {
|
export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO): TDynamicProviderFns => {
|
||||||
const validateProviderInputs = async (inputs: unknown) => {
|
const validateProviderInputs = async (inputs: unknown) => {
|
||||||
const providerInputs = await DynamicSecretSqlDBSchema.parseAsync(inputs);
|
const providerInputs = await DynamicSecretSqlDBSchema.parseAsync(inputs);
|
||||||
|
|
||||||
const [hostIp] = await verifyHostInputValidity(providerInputs.host, Boolean(providerInputs.projectGatewayId));
|
const [hostIp] = await verifyHostInputValidity(providerInputs.host, Boolean(providerInputs.gatewayId));
|
||||||
validateHandlebarTemplate("SQL creation", providerInputs.creationStatement, {
|
validateHandlebarTemplate("SQL creation", providerInputs.creationStatement, {
|
||||||
allowedExpressions: (val) => ["username", "password", "expiration", "database"].includes(val)
|
allowedExpressions: (val) => ["username", "password", "expiration", "database"].includes(val)
|
||||||
});
|
});
|
||||||
@@ -168,7 +168,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
providerInputs: z.infer<typeof DynamicSecretSqlDBSchema>,
|
providerInputs: z.infer<typeof DynamicSecretSqlDBSchema>,
|
||||||
gatewayCallback: (host: string, port: number) => Promise<void>
|
gatewayCallback: (host: string, port: number) => Promise<void>
|
||||||
) => {
|
) => {
|
||||||
const relayDetails = await gatewayService.fnGetGatewayClientTls(providerInputs.projectGatewayId as string);
|
const relayDetails = await gatewayService.fnGetGatewayClientTlsByGatewayId(providerInputs.gatewayId as string);
|
||||||
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
|
||||||
await withGatewayProxy(
|
await withGatewayProxy(
|
||||||
async (port) => {
|
async (port) => {
|
||||||
@@ -202,7 +202,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
await db.destroy();
|
await db.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (providerInputs.projectGatewayId) {
|
if (providerInputs.gatewayId) {
|
||||||
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
||||||
} else {
|
} else {
|
||||||
await gatewayCallback();
|
await gatewayCallback();
|
||||||
@@ -238,7 +238,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
await db.destroy();
|
await db.destroy();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (providerInputs.projectGatewayId) {
|
if (providerInputs.gatewayId) {
|
||||||
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
||||||
} else {
|
} else {
|
||||||
await gatewayCallback();
|
await gatewayCallback();
|
||||||
@@ -265,7 +265,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
await db.destroy();
|
await db.destroy();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (providerInputs.projectGatewayId) {
|
if (providerInputs.gatewayId) {
|
||||||
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
||||||
} else {
|
} else {
|
||||||
await gatewayCallback();
|
await gatewayCallback();
|
||||||
@@ -301,7 +301,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
await db.destroy();
|
await db.destroy();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (providerInputs.projectGatewayId) {
|
if (providerInputs.gatewayId) {
|
||||||
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
await gatewayProxyWrapper(providerInputs, gatewayCallback);
|
||||||
} else {
|
} else {
|
||||||
await gatewayCallback();
|
await gatewayCallback();
|
||||||
|
|||||||
@@ -1,37 +1,34 @@
|
|||||||
import { Knex } from "knex";
|
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { GatewaysSchema, TableName, TGateways } from "@app/db/schemas";
|
import { GatewaysSchema, TableName, TGateways } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import {
|
import { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt } from "@app/lib/knex";
|
||||||
buildFindFilter,
|
|
||||||
ormify,
|
|
||||||
selectAllTableCols,
|
|
||||||
sqlNestRelationships,
|
|
||||||
TFindFilter,
|
|
||||||
TFindOpt
|
|
||||||
} from "@app/lib/knex";
|
|
||||||
|
|
||||||
export type TGatewayDALFactory = ReturnType<typeof gatewayDALFactory>;
|
export type TGatewayDALFactory = ReturnType<typeof gatewayDALFactory>;
|
||||||
|
|
||||||
export const gatewayDALFactory = (db: TDbClient) => {
|
export const gatewayDALFactory = (db: TDbClient) => {
|
||||||
const orm = ormify(db, TableName.Gateway);
|
const orm = ormify(db, TableName.Gateway);
|
||||||
|
|
||||||
const find = async (filter: TFindFilter<TGateways>, { offset, limit, sort, tx }: TFindOpt<TGateways> = {}) => {
|
const find = async (
|
||||||
|
filter: TFindFilter<TGateways> & { orgId?: string },
|
||||||
|
{ offset, limit, sort, tx }: TFindOpt<TGateways> = {}
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const query = (tx || db)(TableName.Gateway)
|
const query = (tx || db)(TableName.Gateway)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
.where(buildFindFilter(filter))
|
.where(buildFindFilter(filter, TableName.Gateway, ["orgId"]))
|
||||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Gateway}.identityId`)
|
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Gateway}.identityId`)
|
||||||
.leftJoin(TableName.ProjectGateway, `${TableName.ProjectGateway}.gatewayId`, `${TableName.Gateway}.id`)
|
.join(
|
||||||
.leftJoin(TableName.Project, `${TableName.Project}.id`, `${TableName.ProjectGateway}.projectId`)
|
TableName.IdentityOrgMembership,
|
||||||
|
`${TableName.IdentityOrgMembership}.identityId`,
|
||||||
|
`${TableName.Gateway}.identityId`
|
||||||
|
)
|
||||||
.select(selectAllTableCols(TableName.Gateway))
|
.select(selectAllTableCols(TableName.Gateway))
|
||||||
.select(
|
.select(db.ref("orgId").withSchema(TableName.IdentityOrgMembership).as("identityOrgId"))
|
||||||
db.ref("name").withSchema(TableName.Identity).as("identityName"),
|
.select(db.ref("name").withSchema(TableName.Identity).as("identityName"));
|
||||||
db.ref("name").withSchema(TableName.Project).as("projectName"),
|
|
||||||
db.ref("slug").withSchema(TableName.Project).as("projectSlug"),
|
if (filter.orgId) {
|
||||||
db.ref("id").withSchema(TableName.Project).as("projectId")
|
void query.where(`${TableName.IdentityOrgMembership}.orgId`, 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);
|
||||||
if (sort) {
|
if (sort) {
|
||||||
@@ -39,48 +36,16 @@ export const gatewayDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const docs = await query;
|
const docs = await query;
|
||||||
return sqlNestRelationships({
|
|
||||||
data: docs,
|
return docs.map((el) => ({
|
||||||
key: "id",
|
...GatewaysSchema.parse(el),
|
||||||
parentMapper: (data) => ({
|
orgId: el.identityOrgId as string, // todo(daniel): figure out why typescript is not inferring this as a string
|
||||||
...GatewaysSchema.parse(data),
|
identity: { id: el.identityId, name: el.identityName }
|
||||||
identity: { id: data.identityId, name: data.identityName }
|
}));
|
||||||
}),
|
|
||||||
childrenMapper: [
|
|
||||||
{
|
|
||||||
key: "projectId",
|
|
||||||
label: "projects" as const,
|
|
||||||
mapper: ({ projectId, projectName, projectSlug }) => ({
|
|
||||||
id: projectId,
|
|
||||||
name: projectName,
|
|
||||||
slug: projectSlug
|
|
||||||
})
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: `${TableName.Gateway}: Find` });
|
throw new DatabaseError({ error, name: `${TableName.Gateway}: Find` });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const findByProjectId = async (projectId: string, tx?: Knex) => {
|
return { ...orm, find };
|
||||||
try {
|
|
||||||
const query = (tx || db)(TableName.Gateway)
|
|
||||||
.join(TableName.Identity, `${TableName.Identity}.id`, `${TableName.Gateway}.identityId`)
|
|
||||||
.join(TableName.ProjectGateway, `${TableName.ProjectGateway}.gatewayId`, `${TableName.Gateway}.id`)
|
|
||||||
.select(selectAllTableCols(TableName.Gateway))
|
|
||||||
.select(
|
|
||||||
db.ref("name").withSchema(TableName.Identity).as("identityName"),
|
|
||||||
db.ref("id").withSchema(TableName.ProjectGateway).as("projectGatewayId")
|
|
||||||
)
|
|
||||||
.where({ [`${TableName.ProjectGateway}.projectId` as "projectId"]: projectId });
|
|
||||||
|
|
||||||
const docs = await query;
|
|
||||||
return docs.map((el) => ({ ...el, identity: { id: el.identityId, name: el.identityName } }));
|
|
||||||
} catch (error) {
|
|
||||||
throw new DatabaseError({ error, name: `${TableName.Gateway}: Find by project id` });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return { ...orm, find, findByProjectId };
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { ForbiddenError } from "@casl/ability";
|
|||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
|
||||||
import { KeyStorePrefixes, PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
|
import { KeyStorePrefixes, PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
@@ -27,17 +26,14 @@ import { TGatewayDALFactory } from "./gateway-dal";
|
|||||||
import {
|
import {
|
||||||
TExchangeAllocatedRelayAddressDTO,
|
TExchangeAllocatedRelayAddressDTO,
|
||||||
TGetGatewayByIdDTO,
|
TGetGatewayByIdDTO,
|
||||||
TGetProjectGatewayByIdDTO,
|
|
||||||
THeartBeatDTO,
|
THeartBeatDTO,
|
||||||
TListGatewaysDTO,
|
TListGatewaysDTO,
|
||||||
TUpdateGatewayByIdDTO
|
TUpdateGatewayByIdDTO
|
||||||
} from "./gateway-types";
|
} from "./gateway-types";
|
||||||
import { TOrgGatewayConfigDALFactory } from "./org-gateway-config-dal";
|
import { TOrgGatewayConfigDALFactory } from "./org-gateway-config-dal";
|
||||||
import { TProjectGatewayDALFactory } from "./project-gateway-dal";
|
|
||||||
|
|
||||||
type TGatewayServiceFactoryDep = {
|
type TGatewayServiceFactoryDep = {
|
||||||
gatewayDAL: TGatewayDALFactory;
|
gatewayDAL: TGatewayDALFactory;
|
||||||
projectGatewayDAL: TProjectGatewayDALFactory;
|
|
||||||
orgGatewayConfigDAL: Pick<TOrgGatewayConfigDALFactory, "findOne" | "create" | "transaction" | "findById">;
|
orgGatewayConfigDAL: Pick<TOrgGatewayConfigDALFactory, "findOne" | "create" | "transaction" | "findById">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "onPremFeatures" | "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "onPremFeatures" | "getPlan">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey" | "decryptWithRootKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey" | "decryptWithRootKey">;
|
||||||
@@ -57,8 +53,7 @@ export const gatewayServiceFactory = ({
|
|||||||
kmsService,
|
kmsService,
|
||||||
permissionService,
|
permissionService,
|
||||||
orgGatewayConfigDAL,
|
orgGatewayConfigDAL,
|
||||||
keyStore,
|
keyStore
|
||||||
projectGatewayDAL
|
|
||||||
}: TGatewayServiceFactoryDep) => {
|
}: TGatewayServiceFactoryDep) => {
|
||||||
const $validateOrgAccessToGateway = async (orgId: string, actorId: string, actorAuthMethod: ActorAuthMethod) => {
|
const $validateOrgAccessToGateway = async (orgId: string, actorId: string, actorAuthMethod: ActorAuthMethod) => {
|
||||||
// if (!licenseService.onPremFeatures.gateway) {
|
// if (!licenseService.onPremFeatures.gateway) {
|
||||||
@@ -526,7 +521,7 @@ export const gatewayServiceFactory = ({
|
|||||||
return gateway;
|
return gateway;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateGatewayById = async ({ orgPermission, id, name, projectIds }: TUpdateGatewayByIdDTO) => {
|
const updateGatewayById = async ({ orgPermission, id, name }: TUpdateGatewayByIdDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
orgPermission.type,
|
orgPermission.type,
|
||||||
orgPermission.id,
|
orgPermission.id,
|
||||||
@@ -543,15 +538,6 @@ export const gatewayServiceFactory = ({
|
|||||||
|
|
||||||
const [gateway] = await gatewayDAL.update({ id, orgGatewayRootCaId: orgGatewayConfig.id }, { name });
|
const [gateway] = await gatewayDAL.update({ id, orgGatewayRootCaId: orgGatewayConfig.id }, { name });
|
||||||
if (!gateway) throw new NotFoundError({ message: `Gateway with ID ${id} not found.` });
|
if (!gateway) throw new NotFoundError({ message: `Gateway with ID ${id} not found.` });
|
||||||
if (projectIds) {
|
|
||||||
await projectGatewayDAL.transaction(async (tx) => {
|
|
||||||
await projectGatewayDAL.delete({ gatewayId: gateway.id }, tx);
|
|
||||||
await projectGatewayDAL.insertMany(
|
|
||||||
projectIds.map((el) => ({ gatewayId: gateway.id, projectId: el })),
|
|
||||||
tx
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return gateway;
|
return gateway;
|
||||||
};
|
};
|
||||||
@@ -576,27 +562,7 @@ export const gatewayServiceFactory = ({
|
|||||||
return gateway;
|
return gateway;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProjectGateways = async ({ projectId, projectPermission }: TGetProjectGatewayByIdDTO) => {
|
const fnGetGatewayClientTlsByGatewayId = async (gatewayId: string) => {
|
||||||
await permissionService.getProjectPermission({
|
|
||||||
projectId,
|
|
||||||
actor: projectPermission.type,
|
|
||||||
actorId: projectPermission.id,
|
|
||||||
actorOrgId: projectPermission.orgId,
|
|
||||||
actorAuthMethod: projectPermission.authMethod,
|
|
||||||
actionProjectType: ActionProjectType.Any
|
|
||||||
});
|
|
||||||
|
|
||||||
const gateways = await gatewayDAL.findByProjectId(projectId);
|
|
||||||
return gateways;
|
|
||||||
};
|
|
||||||
|
|
||||||
// this has no permission check and used for dynamic secrets directly
|
|
||||||
// assumes permission check is already done
|
|
||||||
const fnGetGatewayClientTls = async (projectGatewayId: string) => {
|
|
||||||
const projectGateway = await projectGatewayDAL.findById(projectGatewayId);
|
|
||||||
if (!projectGateway) throw new NotFoundError({ message: `Project gateway with ID ${projectGatewayId} not found.` });
|
|
||||||
|
|
||||||
const { gatewayId } = projectGateway;
|
|
||||||
const gateway = await gatewayDAL.findById(gatewayId);
|
const gateway = await gatewayDAL.findById(gatewayId);
|
||||||
if (!gateway) throw new NotFoundError({ message: `Gateway with ID ${gatewayId} not found.` });
|
if (!gateway) throw new NotFoundError({ message: `Gateway with ID ${gatewayId} not found.` });
|
||||||
|
|
||||||
@@ -645,8 +611,7 @@ export const gatewayServiceFactory = ({
|
|||||||
getGatewayById,
|
getGatewayById,
|
||||||
updateGatewayById,
|
updateGatewayById,
|
||||||
deleteGatewayById,
|
deleteGatewayById,
|
||||||
getProjectGateways,
|
fnGetGatewayClientTlsByGatewayId,
|
||||||
fnGetGatewayClientTls,
|
|
||||||
heartbeat
|
heartbeat
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ export type TGetGatewayByIdDTO = {
|
|||||||
export type TUpdateGatewayByIdDTO = {
|
export type TUpdateGatewayByIdDTO = {
|
||||||
id: string;
|
id: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
projectIds?: string[];
|
|
||||||
orgPermission: OrgServiceActor;
|
orgPermission: OrgServiceActor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import { TDbClient } from "@app/db";
|
|
||||||
import { TableName } from "@app/db/schemas";
|
|
||||||
import { ormify } from "@app/lib/knex";
|
|
||||||
|
|
||||||
export type TProjectGatewayDALFactory = ReturnType<typeof projectGatewayDALFactory>;
|
|
||||||
|
|
||||||
export const projectGatewayDALFactory = (db: TDbClient) => {
|
|
||||||
const orm = ormify(db, TableName.ProjectGateway);
|
|
||||||
return orm;
|
|
||||||
};
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { Octokit } from "@octokit/core";
|
import { Octokit } from "@octokit/core";
|
||||||
import { paginateGraphQL } from "@octokit/plugin-paginate-graphql";
|
import { paginateGraphql } from "@octokit/plugin-paginate-graphql";
|
||||||
import { Octokit as OctokitRest } from "@octokit/rest";
|
import { Octokit as OctokitRest } from "@octokit/rest";
|
||||||
|
|
||||||
import { OrgMembershipRole } from "@app/db/schemas";
|
import { OrgMembershipRole } from "@app/db/schemas";
|
||||||
@@ -18,7 +18,7 @@ import { TPermissionServiceFactory } from "../permission/permission-service";
|
|||||||
import { TGithubOrgSyncDALFactory } from "./github-org-sync-dal";
|
import { TGithubOrgSyncDALFactory } from "./github-org-sync-dal";
|
||||||
import { TCreateGithubOrgSyncDTO, TDeleteGithubOrgSyncDTO, TUpdateGithubOrgSyncDTO } from "./github-org-sync-types";
|
import { TCreateGithubOrgSyncDTO, TDeleteGithubOrgSyncDTO, TUpdateGithubOrgSyncDTO } from "./github-org-sync-types";
|
||||||
|
|
||||||
const OctokitWithPlugin = Octokit.plugin(paginateGraphQL);
|
const OctokitWithPlugin = Octokit.plugin(paginateGraphql);
|
||||||
|
|
||||||
type TGithubOrgSyncServiceFactoryDep = {
|
type TGithubOrgSyncServiceFactoryDep = {
|
||||||
githubOrgSyncDAL: TGithubOrgSyncDALFactory;
|
githubOrgSyncDAL: TGithubOrgSyncDALFactory;
|
||||||
|
|||||||
@@ -157,10 +157,23 @@ export const groupDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const findGroupsByProjectId = async (projectId: string, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const docs = await (tx || db.replicaNode())(TableName.Groups)
|
||||||
|
.join(TableName.GroupProjectMembership, `${TableName.Groups}.id`, `${TableName.GroupProjectMembership}.groupId`)
|
||||||
|
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
|
||||||
|
.select(selectAllTableCols(TableName.Groups));
|
||||||
|
return docs;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "Find groups by project id" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
findGroups,
|
findGroups,
|
||||||
findByOrgId,
|
findByOrgId,
|
||||||
findAllGroupPossibleMembers,
|
findAllGroupPossibleMembers,
|
||||||
|
findGroupsByProjectId,
|
||||||
...groupOrm
|
...groupOrm
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -176,7 +176,8 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
|
|||||||
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.OrgMembership).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")
|
||||||
);
|
);
|
||||||
|
|
||||||
return docs;
|
return docs;
|
||||||
|
|||||||
@@ -24,9 +24,13 @@ export const initializeHsmModule = (envConfig: Pick<TEnvConfig, "isHsmConfigured
|
|||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
|
|
||||||
logger.info("PKCS#11 module initialized");
|
logger.info("PKCS#11 module initialized");
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
logger.error(err, "Failed to initialize PKCS#11 module");
|
if (error instanceof pkcs11js.Pkcs11Error && error.code === pkcs11js.CKR_CRYPTOKI_ALREADY_INITIALIZED) {
|
||||||
throw err;
|
logger.info("Skipping HSM initialization because it's already initialized.");
|
||||||
|
} else {
|
||||||
|
logger.error(error, "Failed to initialize PKCS#11 module");
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ export type TLDAPConfig = {
|
|||||||
caCert: string;
|
caCert: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TTestLDAPConfigDTO = Omit<
|
||||||
|
TLDAPConfig,
|
||||||
|
"organization" | "id" | "groupSearchBase" | "groupSearchFilter" | "isActive" | "uniqueUserAttribute" | "searchBase"
|
||||||
|
>;
|
||||||
|
|
||||||
export type TCreateLdapCfgDTO = {
|
export type TCreateLdapCfgDTO = {
|
||||||
orgId: string;
|
orgId: string;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
|
|||||||
@@ -2,15 +2,14 @@ import ldapjs from "ldapjs";
|
|||||||
|
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
|
|
||||||
import { TLDAPConfig } from "./ldap-config-types";
|
import { TLDAPConfig, TTestLDAPConfigDTO } from "./ldap-config-types";
|
||||||
|
|
||||||
export const isValidLdapFilter = (filter: string) => {
|
export const isValidLdapFilter = (filter: string) => {
|
||||||
try {
|
try {
|
||||||
ldapjs.parseFilter(filter);
|
ldapjs.parseFilter(filter);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Invalid LDAP filter");
|
logger.error(error, "Invalid LDAP filter");
|
||||||
logger.error(error);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -20,7 +19,7 @@ export const isValidLdapFilter = (filter: string) => {
|
|||||||
* @param ldapConfig - The LDAP configuration to test
|
* @param ldapConfig - The LDAP configuration to test
|
||||||
* @returns {Boolean} isConnected - Whether or not the connection was successful
|
* @returns {Boolean} isConnected - Whether or not the connection was successful
|
||||||
*/
|
*/
|
||||||
export const testLDAPConfig = async (ldapConfig: TLDAPConfig): Promise<boolean> => {
|
export const testLDAPConfig = async (ldapConfig: TTestLDAPConfigDTO): Promise<boolean> => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const ldapClient = ldapjs.createClient({
|
const ldapClient = ldapjs.createClient({
|
||||||
url: ldapConfig.url,
|
url: ldapConfig.url,
|
||||||
|
|||||||
@@ -714,13 +714,15 @@ export const oidcConfigServiceFactory = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const groups = typeof claims.groups === "string" ? [claims.groups] : (claims.groups as string[] | undefined);
|
||||||
|
|
||||||
oidcLogin({
|
oidcLogin({
|
||||||
email: claims.email,
|
email: claims.email,
|
||||||
externalId: claims.sub,
|
externalId: claims.sub,
|
||||||
firstName: claims.given_name ?? "",
|
firstName: claims.given_name ?? "",
|
||||||
lastName: claims.family_name ?? "",
|
lastName: claims.family_name ?? "",
|
||||||
orgId: org.id,
|
orgId: org.id,
|
||||||
groups: claims.groups as string[] | undefined,
|
groups,
|
||||||
callbackPort,
|
callbackPort,
|
||||||
manageGroupMemberships: oidcCfg.manageGroupMemberships
|
manageGroupMemberships: oidcCfg.manageGroupMemberships
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,473 @@
|
|||||||
|
import { AbilityBuilder, createMongoAbility, MongoAbility } from "@casl/ability";
|
||||||
|
|
||||||
|
import {
|
||||||
|
ProjectPermissionActions,
|
||||||
|
ProjectPermissionCertificateActions,
|
||||||
|
ProjectPermissionCmekActions,
|
||||||
|
ProjectPermissionCommitsActions,
|
||||||
|
ProjectPermissionDynamicSecretActions,
|
||||||
|
ProjectPermissionGroupActions,
|
||||||
|
ProjectPermissionIdentityActions,
|
||||||
|
ProjectPermissionKmipActions,
|
||||||
|
ProjectPermissionMemberActions,
|
||||||
|
ProjectPermissionPkiSubscriberActions,
|
||||||
|
ProjectPermissionSecretActions,
|
||||||
|
ProjectPermissionSecretRotationActions,
|
||||||
|
ProjectPermissionSecretSyncActions,
|
||||||
|
ProjectPermissionSet,
|
||||||
|
ProjectPermissionSshHostActions,
|
||||||
|
ProjectPermissionSub
|
||||||
|
} from "@app/ee/services/permission/project-permission";
|
||||||
|
|
||||||
|
const buildAdminPermissionRules = () => {
|
||||||
|
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||||
|
|
||||||
|
// Admins get full access to everything
|
||||||
|
[
|
||||||
|
ProjectPermissionSub.SecretFolders,
|
||||||
|
ProjectPermissionSub.SecretImports,
|
||||||
|
ProjectPermissionSub.SecretApproval,
|
||||||
|
ProjectPermissionSub.Role,
|
||||||
|
ProjectPermissionSub.Integrations,
|
||||||
|
ProjectPermissionSub.Webhooks,
|
||||||
|
ProjectPermissionSub.ServiceTokens,
|
||||||
|
ProjectPermissionSub.Settings,
|
||||||
|
ProjectPermissionSub.Environments,
|
||||||
|
ProjectPermissionSub.Tags,
|
||||||
|
ProjectPermissionSub.AuditLogs,
|
||||||
|
ProjectPermissionSub.IpAllowList,
|
||||||
|
ProjectPermissionSub.CertificateAuthorities,
|
||||||
|
ProjectPermissionSub.CertificateTemplates,
|
||||||
|
ProjectPermissionSub.PkiAlerts,
|
||||||
|
ProjectPermissionSub.PkiCollections,
|
||||||
|
ProjectPermissionSub.SshCertificateAuthorities,
|
||||||
|
ProjectPermissionSub.SshCertificates,
|
||||||
|
ProjectPermissionSub.SshCertificateTemplates,
|
||||||
|
ProjectPermissionSub.SshHostGroups
|
||||||
|
].forEach((el) => {
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionActions.Delete
|
||||||
|
],
|
||||||
|
el
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionCertificateActions.Read,
|
||||||
|
ProjectPermissionCertificateActions.Edit,
|
||||||
|
ProjectPermissionCertificateActions.Create,
|
||||||
|
ProjectPermissionCertificateActions.Delete,
|
||||||
|
ProjectPermissionCertificateActions.ReadPrivateKey
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Certificates
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[ProjectPermissionCommitsActions.Read, ProjectPermissionCommitsActions.PerformRollback],
|
||||||
|
ProjectPermissionSub.Commits
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionSshHostActions.Edit,
|
||||||
|
ProjectPermissionSshHostActions.Read,
|
||||||
|
ProjectPermissionSshHostActions.Create,
|
||||||
|
ProjectPermissionSshHostActions.Delete,
|
||||||
|
ProjectPermissionSshHostActions.IssueHostCert
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.SshHosts
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionPkiSubscriberActions.Edit,
|
||||||
|
ProjectPermissionPkiSubscriberActions.Read,
|
||||||
|
ProjectPermissionPkiSubscriberActions.Create,
|
||||||
|
ProjectPermissionPkiSubscriberActions.Delete,
|
||||||
|
ProjectPermissionPkiSubscriberActions.IssueCert,
|
||||||
|
ProjectPermissionPkiSubscriberActions.ListCerts
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.PkiSubscribers
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionMemberActions.Create,
|
||||||
|
ProjectPermissionMemberActions.Edit,
|
||||||
|
ProjectPermissionMemberActions.Delete,
|
||||||
|
ProjectPermissionMemberActions.Read,
|
||||||
|
ProjectPermissionMemberActions.GrantPrivileges,
|
||||||
|
ProjectPermissionMemberActions.AssumePrivileges
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Member
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionGroupActions.Create,
|
||||||
|
ProjectPermissionGroupActions.Edit,
|
||||||
|
ProjectPermissionGroupActions.Delete,
|
||||||
|
ProjectPermissionGroupActions.Read,
|
||||||
|
ProjectPermissionGroupActions.GrantPrivileges
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Groups
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionIdentityActions.Create,
|
||||||
|
ProjectPermissionIdentityActions.Edit,
|
||||||
|
ProjectPermissionIdentityActions.Delete,
|
||||||
|
ProjectPermissionIdentityActions.Read,
|
||||||
|
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||||
|
ProjectPermissionIdentityActions.AssumePrivileges
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Identity
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionSecretActions.DescribeSecret,
|
||||||
|
ProjectPermissionSecretActions.ReadValue,
|
||||||
|
ProjectPermissionSecretActions.Create,
|
||||||
|
ProjectPermissionSecretActions.Edit,
|
||||||
|
ProjectPermissionSecretActions.Delete
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Secrets
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionDynamicSecretActions.ReadRootCredential,
|
||||||
|
ProjectPermissionDynamicSecretActions.EditRootCredential,
|
||||||
|
ProjectPermissionDynamicSecretActions.CreateRootCredential,
|
||||||
|
ProjectPermissionDynamicSecretActions.DeleteRootCredential,
|
||||||
|
ProjectPermissionDynamicSecretActions.Lease
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.DynamicSecrets
|
||||||
|
);
|
||||||
|
|
||||||
|
can([ProjectPermissionActions.Edit, ProjectPermissionActions.Delete], ProjectPermissionSub.Project);
|
||||||
|
can([ProjectPermissionActions.Read, ProjectPermissionActions.Create], ProjectPermissionSub.SecretRollback);
|
||||||
|
can([ProjectPermissionActions.Edit], ProjectPermissionSub.Kms);
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionCmekActions.Create,
|
||||||
|
ProjectPermissionCmekActions.Edit,
|
||||||
|
ProjectPermissionCmekActions.Delete,
|
||||||
|
ProjectPermissionCmekActions.Read,
|
||||||
|
ProjectPermissionCmekActions.Encrypt,
|
||||||
|
ProjectPermissionCmekActions.Decrypt,
|
||||||
|
ProjectPermissionCmekActions.Sign,
|
||||||
|
ProjectPermissionCmekActions.Verify
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Cmek
|
||||||
|
);
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionSecretSyncActions.Create,
|
||||||
|
ProjectPermissionSecretSyncActions.Edit,
|
||||||
|
ProjectPermissionSecretSyncActions.Delete,
|
||||||
|
ProjectPermissionSecretSyncActions.Read,
|
||||||
|
ProjectPermissionSecretSyncActions.SyncSecrets,
|
||||||
|
ProjectPermissionSecretSyncActions.ImportSecrets,
|
||||||
|
ProjectPermissionSecretSyncActions.RemoveSecrets
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.SecretSyncs
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionKmipActions.CreateClients,
|
||||||
|
ProjectPermissionKmipActions.UpdateClients,
|
||||||
|
ProjectPermissionKmipActions.DeleteClients,
|
||||||
|
ProjectPermissionKmipActions.ReadClients,
|
||||||
|
ProjectPermissionKmipActions.GenerateClientCertificates
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Kmip
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionSecretRotationActions.Create,
|
||||||
|
ProjectPermissionSecretRotationActions.Edit,
|
||||||
|
ProjectPermissionSecretRotationActions.Delete,
|
||||||
|
ProjectPermissionSecretRotationActions.Read,
|
||||||
|
ProjectPermissionSecretRotationActions.ReadGeneratedCredentials,
|
||||||
|
ProjectPermissionSecretRotationActions.RotateSecrets
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.SecretRotation
|
||||||
|
);
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildMemberPermissionRules = () => {
|
||||||
|
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionSecretActions.DescribeSecret,
|
||||||
|
ProjectPermissionSecretActions.ReadValue,
|
||||||
|
ProjectPermissionSecretActions.Edit,
|
||||||
|
ProjectPermissionSecretActions.Create,
|
||||||
|
ProjectPermissionSecretActions.Delete
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Secrets
|
||||||
|
);
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionActions.Delete
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.SecretFolders
|
||||||
|
);
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionDynamicSecretActions.ReadRootCredential,
|
||||||
|
ProjectPermissionDynamicSecretActions.EditRootCredential,
|
||||||
|
ProjectPermissionDynamicSecretActions.CreateRootCredential,
|
||||||
|
ProjectPermissionDynamicSecretActions.DeleteRootCredential,
|
||||||
|
ProjectPermissionDynamicSecretActions.Lease
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.DynamicSecrets
|
||||||
|
);
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionActions.Delete
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.SecretImports
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[ProjectPermissionCommitsActions.Read, ProjectPermissionCommitsActions.PerformRollback],
|
||||||
|
ProjectPermissionSub.Commits
|
||||||
|
);
|
||||||
|
|
||||||
|
can([ProjectPermissionActions.Read], ProjectPermissionSub.SecretApproval);
|
||||||
|
can([ProjectPermissionSecretRotationActions.Read], ProjectPermissionSub.SecretRotation);
|
||||||
|
|
||||||
|
can([ProjectPermissionActions.Read, ProjectPermissionActions.Create], ProjectPermissionSub.SecretRollback);
|
||||||
|
|
||||||
|
can([ProjectPermissionMemberActions.Read, ProjectPermissionMemberActions.Create], ProjectPermissionSub.Member);
|
||||||
|
|
||||||
|
can([ProjectPermissionGroupActions.Read], ProjectPermissionSub.Groups);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionActions.Delete
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Integrations
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionActions.Delete
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Webhooks
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionIdentityActions.Read,
|
||||||
|
ProjectPermissionIdentityActions.Edit,
|
||||||
|
ProjectPermissionIdentityActions.Create,
|
||||||
|
ProjectPermissionIdentityActions.Delete
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Identity
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionActions.Delete
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.ServiceTokens
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionActions.Delete
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Settings
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionActions.Delete
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Environments
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionActions.Delete
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Tags
|
||||||
|
);
|
||||||
|
|
||||||
|
can([ProjectPermissionActions.Read], ProjectPermissionSub.Role);
|
||||||
|
can([ProjectPermissionActions.Read], ProjectPermissionSub.AuditLogs);
|
||||||
|
can([ProjectPermissionActions.Read], ProjectPermissionSub.IpAllowList);
|
||||||
|
|
||||||
|
// double check if all CRUD are needed for CA and Certificates
|
||||||
|
can([ProjectPermissionActions.Read], ProjectPermissionSub.CertificateAuthorities);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionCertificateActions.Read,
|
||||||
|
ProjectPermissionCertificateActions.Edit,
|
||||||
|
ProjectPermissionCertificateActions.Create,
|
||||||
|
ProjectPermissionCertificateActions.Delete
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Certificates
|
||||||
|
);
|
||||||
|
|
||||||
|
can([ProjectPermissionActions.Read], ProjectPermissionSub.CertificateTemplates);
|
||||||
|
|
||||||
|
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiAlerts);
|
||||||
|
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiCollections);
|
||||||
|
|
||||||
|
can([ProjectPermissionActions.Read], ProjectPermissionSub.SshCertificates);
|
||||||
|
can([ProjectPermissionActions.Create], ProjectPermissionSub.SshCertificates);
|
||||||
|
can([ProjectPermissionActions.Read], ProjectPermissionSub.SshCertificateTemplates);
|
||||||
|
|
||||||
|
can([ProjectPermissionSshHostActions.Read], ProjectPermissionSub.SshHosts);
|
||||||
|
can([ProjectPermissionPkiSubscriberActions.Read], ProjectPermissionSub.PkiSubscribers);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionCmekActions.Create,
|
||||||
|
ProjectPermissionCmekActions.Edit,
|
||||||
|
ProjectPermissionCmekActions.Delete,
|
||||||
|
ProjectPermissionCmekActions.Read,
|
||||||
|
ProjectPermissionCmekActions.Encrypt,
|
||||||
|
ProjectPermissionCmekActions.Decrypt,
|
||||||
|
ProjectPermissionCmekActions.Sign,
|
||||||
|
ProjectPermissionCmekActions.Verify
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Cmek
|
||||||
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionSecretSyncActions.Create,
|
||||||
|
ProjectPermissionSecretSyncActions.Edit,
|
||||||
|
ProjectPermissionSecretSyncActions.Delete,
|
||||||
|
ProjectPermissionSecretSyncActions.Read,
|
||||||
|
ProjectPermissionSecretSyncActions.SyncSecrets,
|
||||||
|
ProjectPermissionSecretSyncActions.ImportSecrets,
|
||||||
|
ProjectPermissionSecretSyncActions.RemoveSecrets
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.SecretSyncs
|
||||||
|
);
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildViewerPermissionRules = () => {
|
||||||
|
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[ProjectPermissionSecretActions.DescribeSecret, ProjectPermissionSecretActions.ReadValue],
|
||||||
|
ProjectPermissionSub.Secrets
|
||||||
|
);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretFolders);
|
||||||
|
can(ProjectPermissionDynamicSecretActions.ReadRootCredential, ProjectPermissionSub.DynamicSecrets);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretImports);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||||
|
can(ProjectPermissionSecretRotationActions.Read, ProjectPermissionSub.SecretRotation);
|
||||||
|
can(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member);
|
||||||
|
can(ProjectPermissionGroupActions.Read, ProjectPermissionSub.Groups);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks);
|
||||||
|
can(ProjectPermissionIdentityActions.Read, ProjectPermissionSub.Identity);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Settings);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Tags);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.CertificateAuthorities);
|
||||||
|
can(ProjectPermissionCertificateActions.Read, ProjectPermissionSub.Certificates);
|
||||||
|
can(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateTemplates);
|
||||||
|
can(ProjectPermissionSecretSyncActions.Read, ProjectPermissionSub.SecretSyncs);
|
||||||
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.Commits);
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildNoAccessProjectPermission = () => {
|
||||||
|
const { rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||||
|
return rules;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildSshHostBootstrapPermissionRules = () => {
|
||||||
|
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[ProjectPermissionSshHostActions.Create, ProjectPermissionSshHostActions.IssueHostCert],
|
||||||
|
ProjectPermissionSub.SshHosts
|
||||||
|
);
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildCryptographicOperatorPermissionRules = () => {
|
||||||
|
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionCmekActions.Encrypt,
|
||||||
|
ProjectPermissionCmekActions.Decrypt,
|
||||||
|
ProjectPermissionCmekActions.Sign,
|
||||||
|
ProjectPermissionCmekActions.Verify
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.Cmek
|
||||||
|
);
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
};
|
||||||
|
|
||||||
|
// General
|
||||||
|
export const projectAdminPermissions = buildAdminPermissionRules();
|
||||||
|
export const projectMemberPermissions = buildMemberPermissionRules();
|
||||||
|
export const projectViewerPermission = buildViewerPermissionRules();
|
||||||
|
export const projectNoAccessPermissions = buildNoAccessProjectPermission();
|
||||||
|
|
||||||
|
// SSH
|
||||||
|
export const sshHostBootstrapPermissions = buildSshHostBootstrapPermissionRules();
|
||||||
|
|
||||||
|
// KMS
|
||||||
|
export const cryptographicOperatorPermissions = buildCryptographicOperatorPermissionRules();
|
||||||
@@ -41,7 +41,8 @@ export enum OrgPermissionGatewayActions {
|
|||||||
CreateGateways = "create-gateways",
|
CreateGateways = "create-gateways",
|
||||||
ListGateways = "list-gateways",
|
ListGateways = "list-gateways",
|
||||||
EditGateways = "edit-gateways",
|
EditGateways = "edit-gateways",
|
||||||
DeleteGateways = "delete-gateways"
|
DeleteGateways = "delete-gateways",
|
||||||
|
AttachGateways = "attach-gateways"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum OrgPermissionIdentityActions {
|
export enum OrgPermissionIdentityActions {
|
||||||
@@ -337,6 +338,7 @@ const buildAdminPermission = () => {
|
|||||||
can(OrgPermissionGatewayActions.CreateGateways, OrgPermissionSubjects.Gateway);
|
can(OrgPermissionGatewayActions.CreateGateways, OrgPermissionSubjects.Gateway);
|
||||||
can(OrgPermissionGatewayActions.EditGateways, OrgPermissionSubjects.Gateway);
|
can(OrgPermissionGatewayActions.EditGateways, OrgPermissionSubjects.Gateway);
|
||||||
can(OrgPermissionGatewayActions.DeleteGateways, OrgPermissionSubjects.Gateway);
|
can(OrgPermissionGatewayActions.DeleteGateways, OrgPermissionSubjects.Gateway);
|
||||||
|
can(OrgPermissionGatewayActions.AttachGateways, OrgPermissionSubjects.Gateway);
|
||||||
|
|
||||||
can(OrgPermissionAdminConsoleAction.AccessAllProjects, OrgPermissionSubjects.AdminConsole);
|
can(OrgPermissionAdminConsoleAction.AccessAllProjects, OrgPermissionSubjects.AdminConsole);
|
||||||
|
|
||||||
@@ -378,6 +380,7 @@ const buildMemberPermission = () => {
|
|||||||
can(OrgPermissionAppConnectionActions.Connect, OrgPermissionSubjects.AppConnections);
|
can(OrgPermissionAppConnectionActions.Connect, OrgPermissionSubjects.AppConnections);
|
||||||
can(OrgPermissionGatewayActions.ListGateways, OrgPermissionSubjects.Gateway);
|
can(OrgPermissionGatewayActions.ListGateways, OrgPermissionSubjects.Gateway);
|
||||||
can(OrgPermissionGatewayActions.CreateGateways, OrgPermissionSubjects.Gateway);
|
can(OrgPermissionGatewayActions.CreateGateways, OrgPermissionSubjects.Gateway);
|
||||||
|
can(OrgPermissionGatewayActions.AttachGateways, OrgPermissionSubjects.Gateway);
|
||||||
|
|
||||||
return rules;
|
return rules;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ export const permissionDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProjectGroupPermissions = async (projectId: string) => {
|
const getProjectGroupPermissions = async (projectId: string, filterGroupId?: string) => {
|
||||||
try {
|
try {
|
||||||
const docs = await db
|
const docs = await db
|
||||||
.replicaNode()(TableName.GroupProjectMembership)
|
.replicaNode()(TableName.GroupProjectMembership)
|
||||||
@@ -148,6 +148,11 @@ export const permissionDALFactory = (db: TDbClient) => {
|
|||||||
`groupCustomRoles.id`
|
`groupCustomRoles.id`
|
||||||
)
|
)
|
||||||
.where(`${TableName.GroupProjectMembership}.projectId`, "=", projectId)
|
.where(`${TableName.GroupProjectMembership}.projectId`, "=", projectId)
|
||||||
|
.where((bd) => {
|
||||||
|
if (filterGroupId) {
|
||||||
|
void bd.where(`${TableName.GroupProjectMembership}.groupId`, "=", filterGroupId);
|
||||||
|
}
|
||||||
|
})
|
||||||
.select(
|
.select(
|
||||||
db.ref("id").withSchema(TableName.GroupProjectMembership).as("membershipId"),
|
db.ref("id").withSchema(TableName.GroupProjectMembership).as("membershipId"),
|
||||||
db.ref("id").withSchema(TableName.Groups).as("groupId"),
|
db.ref("id").withSchema(TableName.Groups).as("groupId"),
|
||||||
|
|||||||
@@ -12,6 +12,14 @@ import {
|
|||||||
TIdentityProjectMemberships,
|
TIdentityProjectMemberships,
|
||||||
TProjectMemberships
|
TProjectMemberships
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
|
import {
|
||||||
|
cryptographicOperatorPermissions,
|
||||||
|
projectAdminPermissions,
|
||||||
|
projectMemberPermissions,
|
||||||
|
projectNoAccessPermissions,
|
||||||
|
projectViewerPermission,
|
||||||
|
sshHostBootstrapPermissions
|
||||||
|
} from "@app/ee/services/permission/default-roles";
|
||||||
import { conditionsMatcher } from "@app/lib/casl";
|
import { conditionsMatcher } from "@app/lib/casl";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { objectify } from "@app/lib/fn";
|
import { objectify } from "@app/lib/fn";
|
||||||
@@ -32,14 +40,7 @@ import {
|
|||||||
TGetServiceTokenProjectPermissionArg,
|
TGetServiceTokenProjectPermissionArg,
|
||||||
TGetUserProjectPermissionArg
|
TGetUserProjectPermissionArg
|
||||||
} from "./permission-service-types";
|
} from "./permission-service-types";
|
||||||
import {
|
import { buildServiceTokenProjectPermission, ProjectPermissionSet } from "./project-permission";
|
||||||
buildServiceTokenProjectPermission,
|
|
||||||
projectAdminPermissions,
|
|
||||||
projectMemberPermissions,
|
|
||||||
projectNoAccessPermissions,
|
|
||||||
ProjectPermissionSet,
|
|
||||||
projectViewerPermission
|
|
||||||
} from "./project-permission";
|
|
||||||
|
|
||||||
type TPermissionServiceFactoryDep = {
|
type TPermissionServiceFactoryDep = {
|
||||||
orgRoleDAL: Pick<TOrgRoleDALFactory, "findOne">;
|
orgRoleDAL: Pick<TOrgRoleDALFactory, "findOne">;
|
||||||
@@ -95,6 +96,10 @@ export const permissionServiceFactory = ({
|
|||||||
return projectViewerPermission;
|
return projectViewerPermission;
|
||||||
case ProjectMembershipRole.NoAccess:
|
case ProjectMembershipRole.NoAccess:
|
||||||
return projectNoAccessPermissions;
|
return projectNoAccessPermissions;
|
||||||
|
case ProjectMembershipRole.SshHostBootstrapper:
|
||||||
|
return sshHostBootstrapPermissions;
|
||||||
|
case ProjectMembershipRole.KmsCryptographicOperator:
|
||||||
|
return cryptographicOperatorPermissions;
|
||||||
case ProjectMembershipRole.Custom: {
|
case ProjectMembershipRole.Custom: {
|
||||||
return unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(
|
return unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(
|
||||||
permissions as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[]
|
permissions as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[]
|
||||||
@@ -625,6 +630,34 @@ export const permissionServiceFactory = ({
|
|||||||
return { permission };
|
return { permission };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const checkGroupProjectPermission = async ({
|
||||||
|
groupId,
|
||||||
|
projectId,
|
||||||
|
checkPermissions
|
||||||
|
}: {
|
||||||
|
groupId: string;
|
||||||
|
projectId: string;
|
||||||
|
checkPermissions: ProjectPermissionSet;
|
||||||
|
}) => {
|
||||||
|
const rawGroupProjectPermissions = await permissionDAL.getProjectGroupPermissions(projectId, groupId);
|
||||||
|
const groupPermissions = rawGroupProjectPermissions.map((groupProjectPermission) => {
|
||||||
|
const rolePermissions =
|
||||||
|
groupProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || [];
|
||||||
|
const rules = buildProjectPermissionRules(rolePermissions);
|
||||||
|
const permission = createMongoAbility<ProjectPermissionSet>(rules, {
|
||||||
|
conditionsMatcher
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
permission,
|
||||||
|
id: groupProjectPermission.groupId,
|
||||||
|
name: groupProjectPermission.username,
|
||||||
|
membershipId: groupProjectPermission.id
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return groupPermissions.some((groupPermission) => groupPermission.permission.can(...checkPermissions));
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getUserOrgPermission,
|
getUserOrgPermission,
|
||||||
getOrgPermission,
|
getOrgPermission,
|
||||||
@@ -634,6 +667,7 @@ export const permissionServiceFactory = ({
|
|||||||
getOrgPermissionByRole,
|
getOrgPermissionByRole,
|
||||||
getProjectPermissionByRole,
|
getProjectPermissionByRole,
|
||||||
buildOrgPermission,
|
buildOrgPermission,
|
||||||
buildProjectPermissionRules
|
buildProjectPermissionRules,
|
||||||
|
checkGroupProjectPermission
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,6 +22,14 @@ export enum ProjectPermissionCommitsActions {
|
|||||||
PerformRollback = "perform-rollback"
|
PerformRollback = "perform-rollback"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ProjectPermissionCertificateActions {
|
||||||
|
Read = "read",
|
||||||
|
Create = "create",
|
||||||
|
Edit = "edit",
|
||||||
|
Delete = "delete",
|
||||||
|
ReadPrivateKey = "read-private-key"
|
||||||
|
}
|
||||||
|
|
||||||
export enum ProjectPermissionSecretActions {
|
export enum ProjectPermissionSecretActions {
|
||||||
DescribeAndReadValue = "read",
|
DescribeAndReadValue = "read",
|
||||||
DescribeSecret = "describeSecret",
|
DescribeSecret = "describeSecret",
|
||||||
@@ -84,6 +92,15 @@ export enum ProjectPermissionSshHostActions {
|
|||||||
IssueHostCert = "issue-host-cert"
|
IssueHostCert = "issue-host-cert"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ProjectPermissionPkiSubscriberActions {
|
||||||
|
Read = "read",
|
||||||
|
Create = "create",
|
||||||
|
Edit = "edit",
|
||||||
|
Delete = "delete",
|
||||||
|
IssueCert = "issue-cert",
|
||||||
|
ListCerts = "list-certs"
|
||||||
|
}
|
||||||
|
|
||||||
export enum ProjectPermissionSecretSyncActions {
|
export enum ProjectPermissionSecretSyncActions {
|
||||||
Read = "read",
|
Read = "read",
|
||||||
Create = "create",
|
Create = "create",
|
||||||
@@ -141,6 +158,7 @@ export enum ProjectPermissionSub {
|
|||||||
SshCertificateTemplates = "ssh-certificate-templates",
|
SshCertificateTemplates = "ssh-certificate-templates",
|
||||||
SshHosts = "ssh-hosts",
|
SshHosts = "ssh-hosts",
|
||||||
SshHostGroups = "ssh-host-groups",
|
SshHostGroups = "ssh-host-groups",
|
||||||
|
PkiSubscribers = "pki-subscribers",
|
||||||
PkiAlerts = "pki-alerts",
|
PkiAlerts = "pki-alerts",
|
||||||
PkiCollections = "pki-collections",
|
PkiCollections = "pki-collections",
|
||||||
Kms = "kms",
|
Kms = "kms",
|
||||||
@@ -188,6 +206,11 @@ export type SshHostSubjectFields = {
|
|||||||
hostname: string;
|
hostname: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PkiSubscriberSubjectFields = {
|
||||||
|
name: string;
|
||||||
|
// (dangtony98): consider adding [commonName] as a subject field in the future
|
||||||
|
};
|
||||||
|
|
||||||
export type ProjectPermissionSet =
|
export type ProjectPermissionSet =
|
||||||
| [
|
| [
|
||||||
ProjectPermissionSecretActions,
|
ProjectPermissionSecretActions,
|
||||||
@@ -238,7 +261,7 @@ export type ProjectPermissionSet =
|
|||||||
ProjectPermissionSub.Identity | (ForcedSubject<ProjectPermissionSub.Identity> & IdentityManagementSubjectFields)
|
ProjectPermissionSub.Identity | (ForcedSubject<ProjectPermissionSub.Identity> & IdentityManagementSubjectFields)
|
||||||
]
|
]
|
||||||
| [ProjectPermissionActions, ProjectPermissionSub.CertificateAuthorities]
|
| [ProjectPermissionActions, ProjectPermissionSub.CertificateAuthorities]
|
||||||
| [ProjectPermissionActions, ProjectPermissionSub.Certificates]
|
| [ProjectPermissionCertificateActions, ProjectPermissionSub.Certificates]
|
||||||
| [ProjectPermissionActions, ProjectPermissionSub.CertificateTemplates]
|
| [ProjectPermissionActions, ProjectPermissionSub.CertificateTemplates]
|
||||||
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificateAuthorities]
|
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificateAuthorities]
|
||||||
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificates]
|
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificates]
|
||||||
@@ -247,6 +270,13 @@ export type ProjectPermissionSet =
|
|||||||
ProjectPermissionSshHostActions,
|
ProjectPermissionSshHostActions,
|
||||||
ProjectPermissionSub.SshHosts | (ForcedSubject<ProjectPermissionSub.SshHosts> & SshHostSubjectFields)
|
ProjectPermissionSub.SshHosts | (ForcedSubject<ProjectPermissionSub.SshHosts> & SshHostSubjectFields)
|
||||||
]
|
]
|
||||||
|
| [
|
||||||
|
ProjectPermissionPkiSubscriberActions,
|
||||||
|
(
|
||||||
|
| ProjectPermissionSub.PkiSubscribers
|
||||||
|
| (ForcedSubject<ProjectPermissionSub.PkiSubscribers> & PkiSubscriberSubjectFields)
|
||||||
|
)
|
||||||
|
]
|
||||||
| [ProjectPermissionActions, ProjectPermissionSub.SshHostGroups]
|
| [ProjectPermissionActions, ProjectPermissionSub.SshHostGroups]
|
||||||
| [ProjectPermissionActions, ProjectPermissionSub.PkiAlerts]
|
| [ProjectPermissionActions, ProjectPermissionSub.PkiAlerts]
|
||||||
| [ProjectPermissionActions, ProjectPermissionSub.PkiCollections]
|
| [ProjectPermissionActions, ProjectPermissionSub.PkiCollections]
|
||||||
@@ -398,6 +428,21 @@ const SshHostConditionSchema = z
|
|||||||
})
|
})
|
||||||
.partial();
|
.partial();
|
||||||
|
|
||||||
|
const PkiSubscriberConditionSchema = z
|
||||||
|
.object({
|
||||||
|
name: z.union([
|
||||||
|
z.string(),
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
|
||||||
|
[PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB],
|
||||||
|
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN]
|
||||||
|
})
|
||||||
|
.partial()
|
||||||
|
])
|
||||||
|
})
|
||||||
|
.partial();
|
||||||
|
|
||||||
const GeneralPermissionSchema = [
|
const GeneralPermissionSchema = [
|
||||||
z.object({
|
z.object({
|
||||||
subject: z.literal(ProjectPermissionSub.SecretApproval).describe("The entity this permission pertains to."),
|
subject: z.literal(ProjectPermissionSub.SecretApproval).describe("The entity this permission pertains to."),
|
||||||
@@ -485,7 +530,7 @@ const GeneralPermissionSchema = [
|
|||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
subject: z.literal(ProjectPermissionSub.Certificates).describe("The entity this permission pertains to."),
|
subject: z.literal(ProjectPermissionSub.Certificates).describe("The entity this permission pertains to."),
|
||||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionActions).describe(
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionCertificateActions).describe(
|
||||||
"Describe what action an entity can take."
|
"Describe what action an entity can take."
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
@@ -668,6 +713,16 @@ export const ProjectPermissionV2Schema = z.discriminatedUnion("subject", [
|
|||||||
"When specified, only matching conditions will be allowed to access given resource."
|
"When specified, only matching conditions will be allowed to access given resource."
|
||||||
).optional()
|
).optional()
|
||||||
}),
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(ProjectPermissionSub.PkiSubscribers).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionPkiSubscriberActions).describe(
|
||||||
|
"Describe what action an entity can take."
|
||||||
|
),
|
||||||
|
inverted: z.boolean().optional().describe("Whether rule allows or forbids."),
|
||||||
|
conditions: PkiSubscriberConditionSchema.describe(
|
||||||
|
"When specified, only matching conditions will be allowed to access given resource."
|
||||||
|
).optional()
|
||||||
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
subject: z.literal(ProjectPermissionSub.SecretRotation).describe("The entity this permission pertains to."),
|
subject: z.literal(ProjectPermissionSub.SecretRotation).describe("The entity this permission pertains to."),
|
||||||
inverted: z.boolean().optional().describe("Whether rule allows or forbids."),
|
inverted: z.boolean().optional().describe("Whether rule allows or forbids."),
|
||||||
@@ -683,404 +738,6 @@ export const ProjectPermissionV2Schema = z.discriminatedUnion("subject", [
|
|||||||
|
|
||||||
export type TProjectPermissionV2Schema = z.infer<typeof ProjectPermissionV2Schema>;
|
export type TProjectPermissionV2Schema = z.infer<typeof ProjectPermissionV2Schema>;
|
||||||
|
|
||||||
const buildAdminPermissionRules = () => {
|
|
||||||
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
|
||||||
|
|
||||||
// Admins get full access to everything
|
|
||||||
[
|
|
||||||
ProjectPermissionSub.SecretFolders,
|
|
||||||
ProjectPermissionSub.SecretImports,
|
|
||||||
ProjectPermissionSub.SecretApproval,
|
|
||||||
ProjectPermissionSub.Role,
|
|
||||||
ProjectPermissionSub.Integrations,
|
|
||||||
ProjectPermissionSub.Webhooks,
|
|
||||||
ProjectPermissionSub.ServiceTokens,
|
|
||||||
ProjectPermissionSub.Settings,
|
|
||||||
ProjectPermissionSub.Environments,
|
|
||||||
ProjectPermissionSub.Tags,
|
|
||||||
ProjectPermissionSub.AuditLogs,
|
|
||||||
ProjectPermissionSub.IpAllowList,
|
|
||||||
ProjectPermissionSub.CertificateAuthorities,
|
|
||||||
ProjectPermissionSub.Certificates,
|
|
||||||
ProjectPermissionSub.CertificateTemplates,
|
|
||||||
ProjectPermissionSub.PkiAlerts,
|
|
||||||
ProjectPermissionSub.PkiCollections,
|
|
||||||
ProjectPermissionSub.SshCertificateAuthorities,
|
|
||||||
ProjectPermissionSub.SshCertificates,
|
|
||||||
ProjectPermissionSub.SshCertificateTemplates,
|
|
||||||
ProjectPermissionSub.SshHostGroups
|
|
||||||
].forEach((el) => {
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionActions.Read,
|
|
||||||
ProjectPermissionActions.Edit,
|
|
||||||
ProjectPermissionActions.Create,
|
|
||||||
ProjectPermissionActions.Delete
|
|
||||||
],
|
|
||||||
el
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionSshHostActions.Edit,
|
|
||||||
ProjectPermissionSshHostActions.Read,
|
|
||||||
ProjectPermissionSshHostActions.Create,
|
|
||||||
ProjectPermissionSshHostActions.Delete,
|
|
||||||
ProjectPermissionSshHostActions.IssueHostCert
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.SshHosts
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionMemberActions.Create,
|
|
||||||
ProjectPermissionMemberActions.Edit,
|
|
||||||
ProjectPermissionMemberActions.Delete,
|
|
||||||
ProjectPermissionMemberActions.Read,
|
|
||||||
ProjectPermissionMemberActions.GrantPrivileges,
|
|
||||||
ProjectPermissionMemberActions.AssumePrivileges
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Member
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionGroupActions.Create,
|
|
||||||
ProjectPermissionGroupActions.Edit,
|
|
||||||
ProjectPermissionGroupActions.Delete,
|
|
||||||
ProjectPermissionGroupActions.Read,
|
|
||||||
ProjectPermissionGroupActions.GrantPrivileges
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Groups
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionIdentityActions.Create,
|
|
||||||
ProjectPermissionIdentityActions.Edit,
|
|
||||||
ProjectPermissionIdentityActions.Delete,
|
|
||||||
ProjectPermissionIdentityActions.Read,
|
|
||||||
ProjectPermissionIdentityActions.GrantPrivileges,
|
|
||||||
ProjectPermissionIdentityActions.AssumePrivileges
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Identity
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionSecretActions.DescribeAndReadValue,
|
|
||||||
ProjectPermissionSecretActions.DescribeSecret,
|
|
||||||
ProjectPermissionSecretActions.ReadValue,
|
|
||||||
ProjectPermissionSecretActions.Create,
|
|
||||||
ProjectPermissionSecretActions.Edit,
|
|
||||||
ProjectPermissionSecretActions.Delete
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Secrets
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionDynamicSecretActions.ReadRootCredential,
|
|
||||||
ProjectPermissionDynamicSecretActions.EditRootCredential,
|
|
||||||
ProjectPermissionDynamicSecretActions.CreateRootCredential,
|
|
||||||
ProjectPermissionDynamicSecretActions.DeleteRootCredential,
|
|
||||||
ProjectPermissionDynamicSecretActions.Lease
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.DynamicSecrets
|
|
||||||
);
|
|
||||||
|
|
||||||
can([ProjectPermissionActions.Edit, ProjectPermissionActions.Delete], ProjectPermissionSub.Project);
|
|
||||||
can([ProjectPermissionActions.Read, ProjectPermissionActions.Create], ProjectPermissionSub.SecretRollback);
|
|
||||||
can([ProjectPermissionActions.Edit], ProjectPermissionSub.Kms);
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionCmekActions.Create,
|
|
||||||
ProjectPermissionCmekActions.Edit,
|
|
||||||
ProjectPermissionCmekActions.Delete,
|
|
||||||
ProjectPermissionCmekActions.Read,
|
|
||||||
ProjectPermissionCmekActions.Encrypt,
|
|
||||||
ProjectPermissionCmekActions.Decrypt,
|
|
||||||
ProjectPermissionCmekActions.Sign,
|
|
||||||
ProjectPermissionCmekActions.Verify
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Cmek
|
|
||||||
);
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionSecretSyncActions.Create,
|
|
||||||
ProjectPermissionSecretSyncActions.Edit,
|
|
||||||
ProjectPermissionSecretSyncActions.Delete,
|
|
||||||
ProjectPermissionSecretSyncActions.Read,
|
|
||||||
ProjectPermissionSecretSyncActions.SyncSecrets,
|
|
||||||
ProjectPermissionSecretSyncActions.ImportSecrets,
|
|
||||||
ProjectPermissionSecretSyncActions.RemoveSecrets
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.SecretSyncs
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionKmipActions.CreateClients,
|
|
||||||
ProjectPermissionKmipActions.UpdateClients,
|
|
||||||
ProjectPermissionKmipActions.DeleteClients,
|
|
||||||
ProjectPermissionKmipActions.ReadClients,
|
|
||||||
ProjectPermissionKmipActions.GenerateClientCertificates
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Kmip
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionSecretRotationActions.Create,
|
|
||||||
ProjectPermissionSecretRotationActions.Edit,
|
|
||||||
ProjectPermissionSecretRotationActions.Delete,
|
|
||||||
ProjectPermissionSecretRotationActions.Read,
|
|
||||||
ProjectPermissionSecretRotationActions.ReadGeneratedCredentials,
|
|
||||||
ProjectPermissionSecretRotationActions.RotateSecrets
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.SecretRotation
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[ProjectPermissionCommitsActions.Read, ProjectPermissionCommitsActions.PerformRollback],
|
|
||||||
ProjectPermissionSub.Commits
|
|
||||||
);
|
|
||||||
|
|
||||||
return rules;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const projectAdminPermissions = buildAdminPermissionRules();
|
|
||||||
|
|
||||||
const buildMemberPermissionRules = () => {
|
|
||||||
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionSecretActions.DescribeAndReadValue,
|
|
||||||
ProjectPermissionSecretActions.DescribeSecret,
|
|
||||||
ProjectPermissionSecretActions.ReadValue,
|
|
||||||
ProjectPermissionSecretActions.Edit,
|
|
||||||
ProjectPermissionSecretActions.Create,
|
|
||||||
ProjectPermissionSecretActions.Delete
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Secrets
|
|
||||||
);
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionActions.Read,
|
|
||||||
ProjectPermissionActions.Edit,
|
|
||||||
ProjectPermissionActions.Create,
|
|
||||||
ProjectPermissionActions.Delete
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.SecretFolders
|
|
||||||
);
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionDynamicSecretActions.ReadRootCredential,
|
|
||||||
ProjectPermissionDynamicSecretActions.EditRootCredential,
|
|
||||||
ProjectPermissionDynamicSecretActions.CreateRootCredential,
|
|
||||||
ProjectPermissionDynamicSecretActions.DeleteRootCredential,
|
|
||||||
ProjectPermissionDynamicSecretActions.Lease
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.DynamicSecrets
|
|
||||||
);
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionActions.Read,
|
|
||||||
ProjectPermissionActions.Edit,
|
|
||||||
ProjectPermissionActions.Create,
|
|
||||||
ProjectPermissionActions.Delete
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.SecretImports
|
|
||||||
);
|
|
||||||
|
|
||||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.SecretApproval);
|
|
||||||
can([ProjectPermissionSecretRotationActions.Read], ProjectPermissionSub.SecretRotation);
|
|
||||||
|
|
||||||
can([ProjectPermissionActions.Read, ProjectPermissionActions.Create], ProjectPermissionSub.SecretRollback);
|
|
||||||
|
|
||||||
can([ProjectPermissionMemberActions.Read, ProjectPermissionMemberActions.Create], ProjectPermissionSub.Member);
|
|
||||||
|
|
||||||
can([ProjectPermissionGroupActions.Read], ProjectPermissionSub.Groups);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionActions.Read,
|
|
||||||
ProjectPermissionActions.Edit,
|
|
||||||
ProjectPermissionActions.Create,
|
|
||||||
ProjectPermissionActions.Delete
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Integrations
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionActions.Read,
|
|
||||||
ProjectPermissionActions.Edit,
|
|
||||||
ProjectPermissionActions.Create,
|
|
||||||
ProjectPermissionActions.Delete
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Webhooks
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionIdentityActions.Read,
|
|
||||||
ProjectPermissionIdentityActions.Edit,
|
|
||||||
ProjectPermissionIdentityActions.Create,
|
|
||||||
ProjectPermissionIdentityActions.Delete
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Identity
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionActions.Read,
|
|
||||||
ProjectPermissionActions.Edit,
|
|
||||||
ProjectPermissionActions.Create,
|
|
||||||
ProjectPermissionActions.Delete
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.ServiceTokens
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionActions.Read,
|
|
||||||
ProjectPermissionActions.Edit,
|
|
||||||
ProjectPermissionActions.Create,
|
|
||||||
ProjectPermissionActions.Delete
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Settings
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionActions.Read,
|
|
||||||
ProjectPermissionActions.Edit,
|
|
||||||
ProjectPermissionActions.Create,
|
|
||||||
ProjectPermissionActions.Delete
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Environments
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionActions.Read,
|
|
||||||
ProjectPermissionActions.Edit,
|
|
||||||
ProjectPermissionActions.Create,
|
|
||||||
ProjectPermissionActions.Delete
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Tags
|
|
||||||
);
|
|
||||||
|
|
||||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.Role);
|
|
||||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.AuditLogs);
|
|
||||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.IpAllowList);
|
|
||||||
|
|
||||||
// double check if all CRUD are needed for CA and Certificates
|
|
||||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.CertificateAuthorities);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionActions.Read,
|
|
||||||
ProjectPermissionActions.Edit,
|
|
||||||
ProjectPermissionActions.Create,
|
|
||||||
ProjectPermissionActions.Delete
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Certificates
|
|
||||||
);
|
|
||||||
|
|
||||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.CertificateTemplates);
|
|
||||||
|
|
||||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiAlerts);
|
|
||||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiCollections);
|
|
||||||
|
|
||||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.SshCertificates);
|
|
||||||
can([ProjectPermissionActions.Create], ProjectPermissionSub.SshCertificates);
|
|
||||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.SshCertificateTemplates);
|
|
||||||
|
|
||||||
can([ProjectPermissionSshHostActions.Read], ProjectPermissionSub.SshHosts);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionCmekActions.Create,
|
|
||||||
ProjectPermissionCmekActions.Edit,
|
|
||||||
ProjectPermissionCmekActions.Delete,
|
|
||||||
ProjectPermissionCmekActions.Read,
|
|
||||||
ProjectPermissionCmekActions.Encrypt,
|
|
||||||
ProjectPermissionCmekActions.Decrypt,
|
|
||||||
ProjectPermissionCmekActions.Sign,
|
|
||||||
ProjectPermissionCmekActions.Verify
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.Cmek
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[
|
|
||||||
ProjectPermissionSecretSyncActions.Create,
|
|
||||||
ProjectPermissionSecretSyncActions.Edit,
|
|
||||||
ProjectPermissionSecretSyncActions.Delete,
|
|
||||||
ProjectPermissionSecretSyncActions.Read,
|
|
||||||
ProjectPermissionSecretSyncActions.SyncSecrets,
|
|
||||||
ProjectPermissionSecretSyncActions.ImportSecrets,
|
|
||||||
ProjectPermissionSecretSyncActions.RemoveSecrets
|
|
||||||
],
|
|
||||||
ProjectPermissionSub.SecretSyncs
|
|
||||||
);
|
|
||||||
|
|
||||||
can(
|
|
||||||
[ProjectPermissionCommitsActions.Read, ProjectPermissionCommitsActions.PerformRollback],
|
|
||||||
ProjectPermissionSub.Commits
|
|
||||||
);
|
|
||||||
|
|
||||||
return rules;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const projectMemberPermissions = buildMemberPermissionRules();
|
|
||||||
|
|
||||||
const buildViewerPermissionRules = () => {
|
|
||||||
const { can, rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
|
||||||
|
|
||||||
can(ProjectPermissionSecretActions.DescribeAndReadValue, ProjectPermissionSub.Secrets);
|
|
||||||
can(ProjectPermissionSecretActions.DescribeSecret, ProjectPermissionSub.Secrets);
|
|
||||||
can(ProjectPermissionSecretActions.ReadValue, ProjectPermissionSub.Secrets);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretFolders);
|
|
||||||
can(ProjectPermissionDynamicSecretActions.ReadRootCredential, ProjectPermissionSub.DynamicSecrets);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretImports);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
|
||||||
can(ProjectPermissionSecretRotationActions.Read, ProjectPermissionSub.SecretRotation);
|
|
||||||
can(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member);
|
|
||||||
can(ProjectPermissionGroupActions.Read, ProjectPermissionSub.Groups);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks);
|
|
||||||
can(ProjectPermissionIdentityActions.Read, ProjectPermissionSub.Identity);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Settings);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Environments);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Tags);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.CertificateAuthorities);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates);
|
|
||||||
can(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates);
|
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateTemplates);
|
|
||||||
can(ProjectPermissionSecretSyncActions.Read, ProjectPermissionSub.SecretSyncs);
|
|
||||||
can(ProjectPermissionCommitsActions.Read, ProjectPermissionSub.Commits);
|
|
||||||
|
|
||||||
return rules;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const projectViewerPermission = buildViewerPermissionRules();
|
|
||||||
|
|
||||||
const buildNoAccessProjectPermission = () => {
|
|
||||||
const { rules } = new AbilityBuilder<MongoAbility<ProjectPermissionSet>>(createMongoAbility);
|
|
||||||
return rules;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildServiceTokenProjectPermission = (
|
export const buildServiceTokenProjectPermission = (
|
||||||
scopes: Array<{ secretPath: string; environment: string }>,
|
scopes: Array<{ secretPath: string; environment: string }>,
|
||||||
permission: string[]
|
permission: string[]
|
||||||
@@ -1122,8 +779,6 @@ export const buildServiceTokenProjectPermission = (
|
|||||||
return build({ conditionsMatcher });
|
return build({ conditionsMatcher });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const projectNoAccessPermissions = buildNoAccessProjectPermission();
|
|
||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,22 +1,27 @@
|
|||||||
import { ProjectTemplateDefaultEnvironments } from "@app/ee/services/project-template/project-template-constants";
|
import { ProjectType } from "@app/db/schemas";
|
||||||
import {
|
import {
|
||||||
InfisicalProjectTemplate,
|
InfisicalProjectTemplate,
|
||||||
TUnpackedPermission
|
TUnpackedPermission
|
||||||
} from "@app/ee/services/project-template/project-template-types";
|
} from "@app/ee/services/project-template/project-template-types";
|
||||||
import { getPredefinedRoles } from "@app/services/project-role/project-role-fns";
|
import { getPredefinedRoles } from "@app/services/project-role/project-role-fns";
|
||||||
|
|
||||||
export const getDefaultProjectTemplate = (orgId: string) => ({
|
import { ProjectTemplateDefaultEnvironments } from "./project-template-constants";
|
||||||
|
|
||||||
|
export const getDefaultProjectTemplate = (orgId: string, type: ProjectType) => ({
|
||||||
id: "b11b49a9-09a9-4443-916a-4246f9ff2c69", // random ID to appease zod
|
id: "b11b49a9-09a9-4443-916a-4246f9ff2c69", // random ID to appease zod
|
||||||
|
type,
|
||||||
name: InfisicalProjectTemplate.Default,
|
name: InfisicalProjectTemplate.Default,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
description: "Infisical's default project template",
|
description: `Infisical's ${type} default project template`,
|
||||||
environments: ProjectTemplateDefaultEnvironments,
|
environments: type === ProjectType.SecretManager ? ProjectTemplateDefaultEnvironments : null,
|
||||||
roles: [...getPredefinedRoles("project-template")].map(({ name, slug, permissions }) => ({
|
roles: [...getPredefinedRoles({ projectId: "project-template", projectType: type })].map(
|
||||||
name,
|
({ name, slug, permissions }) => ({
|
||||||
slug,
|
name,
|
||||||
permissions: permissions as TUnpackedPermission[]
|
slug,
|
||||||
})),
|
permissions: permissions as TUnpackedPermission[]
|
||||||
|
})
|
||||||
|
),
|
||||||
orgId
|
orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { packRules } from "@casl/ability/extra";
|
import { packRules } from "@casl/ability/extra";
|
||||||
|
|
||||||
import { TProjectTemplates } from "@app/db/schemas";
|
import { ProjectType, TProjectTemplates } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
|
import { ProjectTemplateDefaultEnvironments } from "@app/ee/services/project-template/project-template-constants";
|
||||||
import { getDefaultProjectTemplate } from "@app/ee/services/project-template/project-template-fns";
|
import { getDefaultProjectTemplate } from "@app/ee/services/project-template/project-template-fns";
|
||||||
import {
|
import {
|
||||||
TCreateProjectTemplateDTO,
|
TCreateProjectTemplateDTO,
|
||||||
@@ -32,11 +33,13 @@ const $unpackProjectTemplate = ({ roles, environments, ...rest }: TProjectTempla
|
|||||||
...rest,
|
...rest,
|
||||||
environments: environments as TProjectTemplateEnvironment[],
|
environments: environments as TProjectTemplateEnvironment[],
|
||||||
roles: [
|
roles: [
|
||||||
...getPredefinedRoles("project-template").map(({ name, slug, permissions }) => ({
|
...getPredefinedRoles({ projectId: "project-template", projectType: rest.type as ProjectType }).map(
|
||||||
name,
|
({ name, slug, permissions }) => ({
|
||||||
slug,
|
name,
|
||||||
permissions: permissions as TUnpackedPermission[]
|
slug,
|
||||||
})),
|
permissions: permissions as TUnpackedPermission[]
|
||||||
|
})
|
||||||
|
),
|
||||||
...(roles as TProjectTemplateRole[]).map((role) => ({
|
...(roles as TProjectTemplateRole[]).map((role) => ({
|
||||||
...role,
|
...role,
|
||||||
permissions: unpackPermissions(role.permissions)
|
permissions: unpackPermissions(role.permissions)
|
||||||
@@ -49,7 +52,7 @@ export const projectTemplateServiceFactory = ({
|
|||||||
permissionService,
|
permissionService,
|
||||||
projectTemplateDAL
|
projectTemplateDAL
|
||||||
}: TProjectTemplatesServiceFactoryDep) => {
|
}: TProjectTemplatesServiceFactoryDep) => {
|
||||||
const listProjectTemplatesByOrg = async (actor: OrgServiceActor) => {
|
const listProjectTemplatesByOrg = async (actor: OrgServiceActor, type?: ProjectType) => {
|
||||||
const plan = await licenseService.getPlan(actor.orgId);
|
const plan = await licenseService.getPlan(actor.orgId);
|
||||||
|
|
||||||
if (!plan.projectTemplates)
|
if (!plan.projectTemplates)
|
||||||
@@ -68,11 +71,14 @@ export const projectTemplateServiceFactory = ({
|
|||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.ProjectTemplates);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.ProjectTemplates);
|
||||||
|
|
||||||
const projectTemplates = await projectTemplateDAL.find({
|
const projectTemplates = await projectTemplateDAL.find({
|
||||||
orgId: actor.orgId
|
orgId: actor.orgId,
|
||||||
|
...(type ? { type } : {})
|
||||||
});
|
});
|
||||||
|
|
||||||
return [
|
return [
|
||||||
getDefaultProjectTemplate(actor.orgId),
|
...(type
|
||||||
|
? [getDefaultProjectTemplate(actor.orgId, type)]
|
||||||
|
: Object.values(ProjectType).map((projectType) => getDefaultProjectTemplate(actor.orgId, projectType))),
|
||||||
...projectTemplates.map((template) => $unpackProjectTemplate(template))
|
...projectTemplates.map((template) => $unpackProjectTemplate(template))
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -134,7 +140,7 @@ export const projectTemplateServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createProjectTemplate = async (
|
const createProjectTemplate = async (
|
||||||
{ roles, environments, ...params }: TCreateProjectTemplateDTO,
|
{ roles, environments, type, ...params }: TCreateProjectTemplateDTO,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
) => {
|
) => {
|
||||||
const plan = await licenseService.getPlan(actor.orgId);
|
const plan = await licenseService.getPlan(actor.orgId);
|
||||||
@@ -154,6 +160,17 @@ export const projectTemplateServiceFactory = ({
|
|||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.ProjectTemplates);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.ProjectTemplates);
|
||||||
|
|
||||||
|
if (environments && type !== ProjectType.SecretManager) {
|
||||||
|
throw new BadRequestError({ message: "Cannot configure environments for non-SecretManager project templates" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (environments && plan.environmentLimit !== null && environments.length > plan.environmentLimit) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
|
message: `Failed to create project template due to environment count exceeding your current limit of ${plan.environmentLimit}. Contact Infisical to increase limit.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const isConflictingName = Boolean(
|
const isConflictingName = Boolean(
|
||||||
await projectTemplateDAL.findOne({
|
await projectTemplateDAL.findOne({
|
||||||
name: params.name,
|
name: params.name,
|
||||||
@@ -169,8 +186,10 @@ export const projectTemplateServiceFactory = ({
|
|||||||
const projectTemplate = await projectTemplateDAL.create({
|
const projectTemplate = await projectTemplateDAL.create({
|
||||||
...params,
|
...params,
|
||||||
roles: JSON.stringify(roles.map((role) => ({ ...role, permissions: packRules(role.permissions) }))),
|
roles: JSON.stringify(roles.map((role) => ({ ...role, permissions: packRules(role.permissions) }))),
|
||||||
environments: JSON.stringify(environments),
|
environments:
|
||||||
orgId: actor.orgId
|
type === ProjectType.SecretManager ? JSON.stringify(environments ?? ProjectTemplateDefaultEnvironments) : null,
|
||||||
|
orgId: actor.orgId,
|
||||||
|
type
|
||||||
});
|
});
|
||||||
|
|
||||||
return $unpackProjectTemplate(projectTemplate);
|
return $unpackProjectTemplate(projectTemplate);
|
||||||
@@ -202,6 +221,19 @@ export const projectTemplateServiceFactory = ({
|
|||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.ProjectTemplates);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.ProjectTemplates);
|
||||||
|
|
||||||
|
if (projectTemplate.type !== ProjectType.SecretManager && environments)
|
||||||
|
throw new BadRequestError({ message: "Cannot configure environments for non-SecretManager project templates" });
|
||||||
|
|
||||||
|
if (projectTemplate.type === ProjectType.SecretManager && environments === null)
|
||||||
|
throw new BadRequestError({ message: "Environments cannot be removed for SecretManager project templates" });
|
||||||
|
|
||||||
|
if (environments && plan.environmentLimit !== null && environments.length > plan.environmentLimit) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
|
message: `Failed to update project template due to environment count exceeding your current limit of ${plan.environmentLimit}. Contact Infisical to increase limit.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (params.name && projectTemplate.name !== params.name) {
|
if (params.name && projectTemplate.name !== params.name) {
|
||||||
const isConflictingName = Boolean(
|
const isConflictingName = Boolean(
|
||||||
await projectTemplateDAL.findOne({
|
await projectTemplateDAL.findOne({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { TProjectEnvironments } from "@app/db/schemas";
|
import { ProjectType, TProjectEnvironments } from "@app/db/schemas";
|
||||||
import { TProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
import { TProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
|
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
|
||||||
|
|
||||||
@@ -15,8 +15,9 @@ export type TProjectTemplateRole = {
|
|||||||
export type TCreateProjectTemplateDTO = {
|
export type TCreateProjectTemplateDTO = {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
type: ProjectType;
|
||||||
roles: TProjectTemplateRole[];
|
roles: TProjectTemplateRole[];
|
||||||
environments: TProjectTemplateEnvironment[];
|
environments?: TProjectTemplateEnvironment[] | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdateProjectTemplateDTO = Partial<TCreateProjectTemplateDTO>;
|
export type TUpdateProjectTemplateDTO = Partial<TCreateProjectTemplateDTO>;
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("secretId").withSchema(TableName.SecretApprovalRequestSecret).as("commitSecretId"),
|
db.ref("secretId").withSchema(TableName.SecretApprovalRequestSecret).as("commitSecretId"),
|
||||||
db.ref("id").withSchema(TableName.SecretApprovalRequestSecret).as("commitId"),
|
db.ref("id").withSchema(TableName.SecretApprovalRequestSecret).as("commitId"),
|
||||||
db.raw(
|
db.raw(
|
||||||
`DENSE_RANK() OVER (partition by ${TableName.Environment}."projectId" ORDER BY ${TableName.SecretApprovalRequest}."id" DESC) as rank`
|
`DENSE_RANK() OVER (PARTITION BY ${TableName.Environment}."projectId" ORDER BY ${TableName.SecretApprovalRequest}."createdAt" DESC) as rank`
|
||||||
),
|
),
|
||||||
db.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
db.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
||||||
db.ref("enforcementLevel").withSchema(TableName.SecretApprovalPolicy).as("policyEnforcementLevel"),
|
db.ref("enforcementLevel").withSchema(TableName.SecretApprovalPolicy).as("policyEnforcementLevel"),
|
||||||
@@ -483,7 +483,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("secretId").withSchema(TableName.SecretApprovalRequestSecretV2).as("commitSecretId"),
|
db.ref("secretId").withSchema(TableName.SecretApprovalRequestSecretV2).as("commitSecretId"),
|
||||||
db.ref("id").withSchema(TableName.SecretApprovalRequestSecretV2).as("commitId"),
|
db.ref("id").withSchema(TableName.SecretApprovalRequestSecretV2).as("commitId"),
|
||||||
db.raw(
|
db.raw(
|
||||||
`DENSE_RANK() OVER (partition by ${TableName.Environment}."projectId" ORDER BY ${TableName.SecretApprovalRequest}."id" DESC) as rank`
|
`DENSE_RANK() OVER (PARTITION BY ${TableName.Environment}."projectId" ORDER BY ${TableName.SecretApprovalRequest}."createdAt" DESC) as rank`
|
||||||
),
|
),
|
||||||
db.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
db.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
||||||
db.ref("allowedSelfApprovals").withSchema(TableName.SecretApprovalPolicy).as("policyAllowedSelfApprovals"),
|
db.ref("allowedSelfApprovals").withSchema(TableName.SecretApprovalPolicy).as("policyAllowedSelfApprovals"),
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
|
||||||
|
export const canUseSecretScanning = (orgId: string) => {
|
||||||
|
const appCfg = getConfig();
|
||||||
|
|
||||||
|
if (!appCfg.isCloud) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return appCfg.SECRET_SCANNING_ORG_WHITELIST?.includes(orgId);
|
||||||
|
};
|
||||||
@@ -12,6 +12,7 @@ import { NotFoundError } from "@app/lib/errors";
|
|||||||
import { TGitAppDALFactory } from "./git-app-dal";
|
import { TGitAppDALFactory } from "./git-app-dal";
|
||||||
import { TGitAppInstallSessionDALFactory } from "./git-app-install-session-dal";
|
import { TGitAppInstallSessionDALFactory } from "./git-app-install-session-dal";
|
||||||
import { TSecretScanningDALFactory } from "./secret-scanning-dal";
|
import { TSecretScanningDALFactory } from "./secret-scanning-dal";
|
||||||
|
import { canUseSecretScanning } from "./secret-scanning-fns";
|
||||||
import { TSecretScanningQueueFactory } from "./secret-scanning-queue";
|
import { TSecretScanningQueueFactory } from "./secret-scanning-queue";
|
||||||
import {
|
import {
|
||||||
SecretScanningRiskStatus,
|
SecretScanningRiskStatus,
|
||||||
@@ -47,12 +48,14 @@ export const secretScanningServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TInstallAppSessionDTO) => {
|
}: TInstallAppSessionDTO) => {
|
||||||
|
const appCfg = getConfig();
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
|
||||||
|
|
||||||
const sessionId = crypto.randomBytes(16).toString("hex");
|
const sessionId = crypto.randomBytes(16).toString("hex");
|
||||||
await gitAppInstallSessionDAL.upsert({ orgId, sessionId, userId: actorId });
|
await gitAppInstallSessionDAL.upsert({ orgId, sessionId, userId: actorId });
|
||||||
return { sessionId };
|
return { sessionId, gitAppSlug: appCfg.SECRET_SCANNING_GIT_APP_SLUG };
|
||||||
};
|
};
|
||||||
|
|
||||||
const linkInstallationToOrg = async ({
|
const linkInstallationToOrg = async ({
|
||||||
@@ -91,7 +94,8 @@ export const secretScanningServiceFactory = ({
|
|||||||
const {
|
const {
|
||||||
data: { repositories }
|
data: { repositories }
|
||||||
} = await octokit.apps.listReposAccessibleToInstallation();
|
} = await octokit.apps.listReposAccessibleToInstallation();
|
||||||
if (appCfg.SECRET_SCANNING_ORG_WHITELIST?.includes(actorOrgId)) {
|
|
||||||
|
if (canUseSecretScanning(actorOrgId)) {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
repositories.map(({ id, full_name }) =>
|
repositories.map(({ id, full_name }) =>
|
||||||
secretScanningQueue.startFullRepoScan({
|
secretScanningQueue.startFullRepoScan({
|
||||||
@@ -102,6 +106,7 @@ export const secretScanningServiceFactory = ({
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { installatedApp };
|
return { installatedApp };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -164,7 +169,6 @@ export const secretScanningServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleRepoPushEvent = async (payload: WebhookEventMap["push"]) => {
|
const handleRepoPushEvent = async (payload: WebhookEventMap["push"]) => {
|
||||||
const appCfg = getConfig();
|
|
||||||
const { commits, repository, installation, pusher } = payload;
|
const { commits, repository, installation, pusher } = payload;
|
||||||
if (!commits || !repository || !installation || !pusher) {
|
if (!commits || !repository || !installation || !pusher) {
|
||||||
return;
|
return;
|
||||||
@@ -175,7 +179,7 @@ export const secretScanningServiceFactory = ({
|
|||||||
});
|
});
|
||||||
if (!installationLink) return;
|
if (!installationLink) return;
|
||||||
|
|
||||||
if (appCfg.SECRET_SCANNING_ORG_WHITELIST?.includes(installationLink.orgId)) {
|
if (canUseSecretScanning(installationLink.orgId)) {
|
||||||
await secretScanningQueue.startPushEventScan({
|
await secretScanningQueue.startPushEventScan({
|
||||||
commits,
|
commits,
|
||||||
pusher: { name: pusher.name, email: pusher.email },
|
pusher: { name: pusher.name, email: pusher.email },
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
||||||
)
|
)
|
||||||
.leftJoin(TableName.Users, `${TableName.SshHostLoginUserMapping}.userId`, `${TableName.Users}.id`)
|
.leftJoin(TableName.Users, `${TableName.SshHostLoginUserMapping}.userId`, `${TableName.Users}.id`)
|
||||||
|
.leftJoin(TableName.Groups, `${TableName.SshHostLoginUserMapping}.groupId`, `${TableName.Groups}.id`)
|
||||||
.where(`${TableName.SshHostGroup}.projectId`, projectId)
|
.where(`${TableName.SshHostGroup}.projectId`, projectId)
|
||||||
.select(
|
.select(
|
||||||
db.ref("id").withSchema(TableName.SshHostGroup).as("sshHostGroupId"),
|
db.ref("id").withSchema(TableName.SshHostGroup).as("sshHostGroupId"),
|
||||||
@@ -35,7 +36,8 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("name").withSchema(TableName.SshHostGroup),
|
db.ref("name").withSchema(TableName.SshHostGroup),
|
||||||
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
||||||
db.ref("username").withSchema(TableName.Users),
|
db.ref("username").withSchema(TableName.Users),
|
||||||
db.ref("userId").withSchema(TableName.SshHostLoginUserMapping)
|
db.ref("userId").withSchema(TableName.SshHostLoginUserMapping),
|
||||||
|
db.ref("slug").withSchema(TableName.Groups).as("groupSlug")
|
||||||
)
|
)
|
||||||
.orderBy(`${TableName.SshHostGroup}.updatedAt`, "desc");
|
.orderBy(`${TableName.SshHostGroup}.updatedAt`, "desc");
|
||||||
|
|
||||||
@@ -69,7 +71,8 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
|
|||||||
const loginMappings = Object.entries(loginMappingGrouped).map(([loginUser, entries]) => ({
|
const loginMappings = Object.entries(loginMappingGrouped).map(([loginUser, entries]) => ({
|
||||||
loginUser,
|
loginUser,
|
||||||
allowedPrincipals: {
|
allowedPrincipals: {
|
||||||
usernames: unique(entries.map((e) => e.username)).filter(Boolean)
|
usernames: unique(entries.map((e) => e.username)).filter(Boolean),
|
||||||
|
groups: unique(entries.map((e) => e.groupSlug)).filter(Boolean)
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
return {
|
return {
|
||||||
@@ -99,6 +102,7 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
||||||
)
|
)
|
||||||
.leftJoin(TableName.Users, `${TableName.SshHostLoginUserMapping}.userId`, `${TableName.Users}.id`)
|
.leftJoin(TableName.Users, `${TableName.SshHostLoginUserMapping}.userId`, `${TableName.Users}.id`)
|
||||||
|
.leftJoin(TableName.Groups, `${TableName.SshHostLoginUserMapping}.groupId`, `${TableName.Groups}.id`)
|
||||||
.where(`${TableName.SshHostGroup}.id`, sshHostGroupId)
|
.where(`${TableName.SshHostGroup}.id`, sshHostGroupId)
|
||||||
.select(
|
.select(
|
||||||
db.ref("id").withSchema(TableName.SshHostGroup).as("sshHostGroupId"),
|
db.ref("id").withSchema(TableName.SshHostGroup).as("sshHostGroupId"),
|
||||||
@@ -106,7 +110,8 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("name").withSchema(TableName.SshHostGroup),
|
db.ref("name").withSchema(TableName.SshHostGroup),
|
||||||
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
||||||
db.ref("username").withSchema(TableName.Users),
|
db.ref("username").withSchema(TableName.Users),
|
||||||
db.ref("userId").withSchema(TableName.SshHostLoginUserMapping)
|
db.ref("userId").withSchema(TableName.SshHostLoginUserMapping),
|
||||||
|
db.ref("slug").withSchema(TableName.Groups).as("groupSlug")
|
||||||
);
|
);
|
||||||
|
|
||||||
if (rows.length === 0) return null;
|
if (rows.length === 0) return null;
|
||||||
@@ -121,7 +126,8 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
|
|||||||
const loginMappings = Object.entries(loginMappingGrouped).map(([loginUser, entries]) => ({
|
const loginMappings = Object.entries(loginMappingGrouped).map(([loginUser, entries]) => ({
|
||||||
loginUser,
|
loginUser,
|
||||||
allowedPrincipals: {
|
allowedPrincipals: {
|
||||||
usernames: unique(entries.map((e) => e.username)).filter(Boolean)
|
usernames: unique(entries.map((e) => e.username)).filter(Boolean),
|
||||||
|
groups: unique(entries.map((e) => e.groupSlug)).filter(Boolean)
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
|||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
|
import { TGroupDALFactory } from "../group/group-dal";
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { createSshLoginMappings } from "../ssh-host/ssh-host-fns";
|
import { createSshLoginMappings } from "../ssh-host/ssh-host-fns";
|
||||||
import {
|
import {
|
||||||
@@ -43,8 +44,12 @@ 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<TPermissionServiceFactory, "getProjectPermission" | "getUserProjectPermission">;
|
permissionService: Pick<
|
||||||
|
TPermissionServiceFactory,
|
||||||
|
"getProjectPermission" | "getUserProjectPermission" | "checkGroupProjectPermission"
|
||||||
|
>;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
|
groupDAL: Pick<TGroupDALFactory, "findGroupsByProjectId">;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TSshHostGroupServiceFactory = ReturnType<typeof sshHostGroupServiceFactory>;
|
export type TSshHostGroupServiceFactory = ReturnType<typeof sshHostGroupServiceFactory>;
|
||||||
@@ -58,7 +63,8 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
sshHostLoginUserMappingDAL,
|
sshHostLoginUserMappingDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService
|
licenseService,
|
||||||
|
groupDAL
|
||||||
}: TSshHostGroupServiceFactoryDep) => {
|
}: TSshHostGroupServiceFactoryDep) => {
|
||||||
const createSshHostGroup = async ({
|
const createSshHostGroup = async ({
|
||||||
projectId,
|
projectId,
|
||||||
@@ -127,6 +133,7 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
loginMappings,
|
loginMappings,
|
||||||
sshHostLoginUserDAL,
|
sshHostLoginUserDAL,
|
||||||
sshHostLoginUserMappingDAL,
|
sshHostLoginUserMappingDAL,
|
||||||
|
groupDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectId,
|
projectId,
|
||||||
@@ -179,13 +186,42 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const updatedSshHostGroup = await sshHostGroupDAL.transaction(async (tx) => {
|
const updatedSshHostGroup = await sshHostGroupDAL.transaction(async (tx) => {
|
||||||
await sshHostGroupDAL.updateById(
|
if (name && name !== sshHostGroup.name) {
|
||||||
sshHostGroupId,
|
// (dangtony98): room to optimize check to ensure that
|
||||||
{
|
// the SSH host group name is unique across the whole org
|
||||||
name
|
const project = await projectDAL.findById(sshHostGroup.projectId, tx);
|
||||||
},
|
if (!project) throw new NotFoundError({ message: `Project with ID '${sshHostGroup.projectId}' not found` });
|
||||||
tx
|
const projects = await projectDAL.find(
|
||||||
);
|
{
|
||||||
|
orgId: project.orgId
|
||||||
|
},
|
||||||
|
{ tx }
|
||||||
|
);
|
||||||
|
|
||||||
|
const existingSshHostGroup = await sshHostGroupDAL.find(
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
$in: {
|
||||||
|
projectId: projects.map((p) => p.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ tx }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existingSshHostGroup.length) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `SSH host group with name '${name}' already exists in the organization`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await sshHostGroupDAL.updateById(
|
||||||
|
sshHostGroupId,
|
||||||
|
{
|
||||||
|
name
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (loginMappings) {
|
if (loginMappings) {
|
||||||
await sshHostLoginUserDAL.delete({ sshHostGroupId: sshHostGroup.id }, tx);
|
await sshHostLoginUserDAL.delete({ sshHostGroupId: sshHostGroup.id }, tx);
|
||||||
if (loginMappings.length) {
|
if (loginMappings.length) {
|
||||||
@@ -194,6 +230,7 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
loginMappings,
|
loginMappings,
|
||||||
sshHostLoginUserDAL,
|
sshHostLoginUserDAL,
|
||||||
sshHostLoginUserMappingDAL,
|
sshHostLoginUserMappingDAL,
|
||||||
|
groupDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectId: sshHostGroup.projectId,
|
projectId: sshHostGroup.projectId,
|
||||||
|
|||||||
@@ -9,12 +9,7 @@ export type TCreateSshHostGroupDTO = {
|
|||||||
export type TUpdateSshHostGroupDTO = {
|
export type TUpdateSshHostGroupDTO = {
|
||||||
sshHostGroupId: string;
|
sshHostGroupId: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
loginMappings?: {
|
loginMappings?: TLoginMapping[];
|
||||||
loginUser: string;
|
|
||||||
allowedPrincipals: {
|
|
||||||
usernames: string[];
|
|
||||||
};
|
|
||||||
}[];
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TGetSshHostGroupDTO = {
|
export type TGetSshHostGroupDTO = {
|
||||||
|
|||||||
@@ -31,8 +31,18 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SshHostLoginUser}.id`,
|
`${TableName.SshHostLoginUser}.id`,
|
||||||
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
||||||
)
|
)
|
||||||
|
.leftJoin(TableName.Users, `${TableName.Users}.id`, `${TableName.SshHostLoginUserMapping}.userId`)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.UserGroupMembership,
|
||||||
|
`${TableName.UserGroupMembership}.groupId`,
|
||||||
|
`${TableName.SshHostLoginUserMapping}.groupId`
|
||||||
|
)
|
||||||
.whereIn(`${TableName.SshHost}.projectId`, projectIds)
|
.whereIn(`${TableName.SshHost}.projectId`, projectIds)
|
||||||
.andWhere(`${TableName.SshHostLoginUserMapping}.userId`, userId)
|
.andWhere((bd) => {
|
||||||
|
void bd
|
||||||
|
.where(`${TableName.SshHostLoginUserMapping}.userId`, userId)
|
||||||
|
.orWhere(`${TableName.UserGroupMembership}.userId`, userId);
|
||||||
|
})
|
||||||
.select(
|
.select(
|
||||||
db.ref("id").withSchema(TableName.SshHost).as("sshHostId"),
|
db.ref("id").withSchema(TableName.SshHost).as("sshHostId"),
|
||||||
db.ref("projectId").withSchema(TableName.SshHost),
|
db.ref("projectId").withSchema(TableName.SshHost),
|
||||||
@@ -58,8 +68,17 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
||||||
)
|
)
|
||||||
.join(TableName.SshHost, `${TableName.SshHostGroupMembership}.sshHostId`, `${TableName.SshHost}.id`)
|
.join(TableName.SshHost, `${TableName.SshHostGroupMembership}.sshHostId`, `${TableName.SshHost}.id`)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.UserGroupMembership,
|
||||||
|
`${TableName.UserGroupMembership}.groupId`,
|
||||||
|
`${TableName.SshHostLoginUserMapping}.groupId`
|
||||||
|
)
|
||||||
.whereIn(`${TableName.SshHost}.projectId`, projectIds)
|
.whereIn(`${TableName.SshHost}.projectId`, projectIds)
|
||||||
.andWhere(`${TableName.SshHostLoginUserMapping}.userId`, userId)
|
.andWhere((bd) => {
|
||||||
|
void bd
|
||||||
|
.where(`${TableName.SshHostLoginUserMapping}.userId`, userId)
|
||||||
|
.orWhere(`${TableName.UserGroupMembership}.userId`, userId);
|
||||||
|
})
|
||||||
.select(
|
.select(
|
||||||
db.ref("id").withSchema(TableName.SshHost).as("sshHostId"),
|
db.ref("id").withSchema(TableName.SshHost).as("sshHostId"),
|
||||||
db.ref("projectId").withSchema(TableName.SshHost),
|
db.ref("projectId").withSchema(TableName.SshHost),
|
||||||
@@ -133,6 +152,7 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
||||||
)
|
)
|
||||||
.leftJoin(TableName.Users, `${TableName.SshHostLoginUserMapping}.userId`, `${TableName.Users}.id`)
|
.leftJoin(TableName.Users, `${TableName.SshHostLoginUserMapping}.userId`, `${TableName.Users}.id`)
|
||||||
|
.leftJoin(TableName.Groups, `${TableName.SshHostLoginUserMapping}.groupId`, `${TableName.Groups}.id`)
|
||||||
.where(`${TableName.SshHost}.projectId`, projectId)
|
.where(`${TableName.SshHost}.projectId`, projectId)
|
||||||
.select(
|
.select(
|
||||||
db.ref("id").withSchema(TableName.SshHost).as("sshHostId"),
|
db.ref("id").withSchema(TableName.SshHost).as("sshHostId"),
|
||||||
@@ -144,6 +164,7 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
||||||
db.ref("username").withSchema(TableName.Users),
|
db.ref("username").withSchema(TableName.Users),
|
||||||
db.ref("userId").withSchema(TableName.SshHostLoginUserMapping),
|
db.ref("userId").withSchema(TableName.SshHostLoginUserMapping),
|
||||||
|
db.ref("slug").withSchema(TableName.Groups).as("groupSlug"),
|
||||||
db.ref("userSshCaId").withSchema(TableName.SshHost),
|
db.ref("userSshCaId").withSchema(TableName.SshHost),
|
||||||
db.ref("hostSshCaId").withSchema(TableName.SshHost)
|
db.ref("hostSshCaId").withSchema(TableName.SshHost)
|
||||||
)
|
)
|
||||||
@@ -163,10 +184,12 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
||||||
)
|
)
|
||||||
.leftJoin(TableName.Users, `${TableName.SshHostLoginUserMapping}.userId`, `${TableName.Users}.id`)
|
.leftJoin(TableName.Users, `${TableName.SshHostLoginUserMapping}.userId`, `${TableName.Users}.id`)
|
||||||
|
.leftJoin(TableName.Groups, `${TableName.SshHostLoginUserMapping}.groupId`, `${TableName.Groups}.id`)
|
||||||
.select(
|
.select(
|
||||||
db.ref("sshHostId").withSchema(TableName.SshHostGroupMembership),
|
db.ref("sshHostId").withSchema(TableName.SshHostGroupMembership),
|
||||||
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
||||||
db.ref("username").withSchema(TableName.Users)
|
db.ref("username").withSchema(TableName.Users),
|
||||||
|
db.ref("slug").withSchema(TableName.Groups).as("groupSlug")
|
||||||
)
|
)
|
||||||
.whereIn(`${TableName.SshHostGroupMembership}.sshHostId`, hostIds);
|
.whereIn(`${TableName.SshHostGroupMembership}.sshHostId`, hostIds);
|
||||||
|
|
||||||
@@ -185,7 +208,8 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
const directMappings = Object.entries(loginMappingGrouped).map(([loginUser, entries]) => ({
|
const directMappings = Object.entries(loginMappingGrouped).map(([loginUser, entries]) => ({
|
||||||
loginUser,
|
loginUser,
|
||||||
allowedPrincipals: {
|
allowedPrincipals: {
|
||||||
usernames: unique(entries.map((e) => e.username)).filter(Boolean)
|
usernames: unique(entries.map((e) => e.username)).filter(Boolean),
|
||||||
|
groups: unique(entries.map((e) => e.groupSlug)).filter(Boolean)
|
||||||
},
|
},
|
||||||
source: LoginMappingSource.HOST
|
source: LoginMappingSource.HOST
|
||||||
}));
|
}));
|
||||||
@@ -197,7 +221,8 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
const groupMappings = Object.entries(inheritedGrouped).map(([loginUser, entries]) => ({
|
const groupMappings = Object.entries(inheritedGrouped).map(([loginUser, entries]) => ({
|
||||||
loginUser,
|
loginUser,
|
||||||
allowedPrincipals: {
|
allowedPrincipals: {
|
||||||
usernames: unique(entries.map((e) => e.username)).filter(Boolean)
|
usernames: unique(entries.map((e) => e.username)).filter(Boolean),
|
||||||
|
groups: unique(entries.map((e) => e.groupSlug)).filter(Boolean)
|
||||||
},
|
},
|
||||||
source: LoginMappingSource.HOST_GROUP
|
source: LoginMappingSource.HOST_GROUP
|
||||||
}));
|
}));
|
||||||
@@ -229,6 +254,7 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
||||||
)
|
)
|
||||||
.leftJoin(TableName.Users, `${TableName.SshHostLoginUserMapping}.userId`, `${TableName.Users}.id`)
|
.leftJoin(TableName.Users, `${TableName.SshHostLoginUserMapping}.userId`, `${TableName.Users}.id`)
|
||||||
|
.leftJoin(TableName.Groups, `${TableName.SshHostLoginUserMapping}.groupId`, `${TableName.Groups}.id`)
|
||||||
.where(`${TableName.SshHost}.id`, sshHostId)
|
.where(`${TableName.SshHost}.id`, sshHostId)
|
||||||
.select(
|
.select(
|
||||||
db.ref("id").withSchema(TableName.SshHost).as("sshHostId"),
|
db.ref("id").withSchema(TableName.SshHost).as("sshHostId"),
|
||||||
@@ -241,7 +267,8 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("username").withSchema(TableName.Users),
|
db.ref("username").withSchema(TableName.Users),
|
||||||
db.ref("userId").withSchema(TableName.SshHostLoginUserMapping),
|
db.ref("userId").withSchema(TableName.SshHostLoginUserMapping),
|
||||||
db.ref("userSshCaId").withSchema(TableName.SshHost),
|
db.ref("userSshCaId").withSchema(TableName.SshHost),
|
||||||
db.ref("hostSshCaId").withSchema(TableName.SshHost)
|
db.ref("hostSshCaId").withSchema(TableName.SshHost),
|
||||||
|
db.ref("slug").withSchema(TableName.Groups).as("groupSlug")
|
||||||
);
|
);
|
||||||
|
|
||||||
if (rows.length === 0) return null;
|
if (rows.length === 0) return null;
|
||||||
@@ -257,7 +284,8 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
const directMappings = Object.entries(directGrouped).map(([loginUser, entries]) => ({
|
const directMappings = Object.entries(directGrouped).map(([loginUser, entries]) => ({
|
||||||
loginUser,
|
loginUser,
|
||||||
allowedPrincipals: {
|
allowedPrincipals: {
|
||||||
usernames: unique(entries.map((e) => e.username)).filter(Boolean)
|
usernames: unique(entries.map((e) => e.username)).filter(Boolean),
|
||||||
|
groups: unique(entries.map((e) => e.groupSlug)).filter(Boolean)
|
||||||
},
|
},
|
||||||
source: LoginMappingSource.HOST
|
source: LoginMappingSource.HOST
|
||||||
}));
|
}));
|
||||||
@@ -275,10 +303,12 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
`${TableName.SshHostLoginUserMapping}.sshHostLoginUserId`
|
||||||
)
|
)
|
||||||
.leftJoin(TableName.Users, `${TableName.SshHostLoginUserMapping}.userId`, `${TableName.Users}.id`)
|
.leftJoin(TableName.Users, `${TableName.SshHostLoginUserMapping}.userId`, `${TableName.Users}.id`)
|
||||||
|
.leftJoin(TableName.Groups, `${TableName.SshHostLoginUserMapping}.groupId`, `${TableName.Groups}.id`)
|
||||||
.where(`${TableName.SshHostGroupMembership}.sshHostId`, sshHostId)
|
.where(`${TableName.SshHostGroupMembership}.sshHostId`, sshHostId)
|
||||||
.select(
|
.select(
|
||||||
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
db.ref("loginUser").withSchema(TableName.SshHostLoginUser),
|
||||||
db.ref("username").withSchema(TableName.Users)
|
db.ref("username").withSchema(TableName.Users),
|
||||||
|
db.ref("slug").withSchema(TableName.Groups).as("groupSlug")
|
||||||
);
|
);
|
||||||
|
|
||||||
const groupGrouped = groupBy(
|
const groupGrouped = groupBy(
|
||||||
@@ -289,7 +319,8 @@ export const sshHostDALFactory = (db: TDbClient) => {
|
|||||||
const groupMappings = Object.entries(groupGrouped).map(([loginUser, entries]) => ({
|
const groupMappings = Object.entries(groupGrouped).map(([loginUser, entries]) => ({
|
||||||
loginUser,
|
loginUser,
|
||||||
allowedPrincipals: {
|
allowedPrincipals: {
|
||||||
usernames: unique(entries.map((e) => e.username)).filter(Boolean)
|
usernames: unique(entries.map((e) => e.username)).filter(Boolean),
|
||||||
|
groups: unique(entries.map((e) => e.groupSlug)).filter(Boolean)
|
||||||
},
|
},
|
||||||
source: LoginMappingSource.HOST_GROUP
|
source: LoginMappingSource.HOST_GROUP
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -3,6 +3,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 { ProjectPermissionSshHostActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||||
import { TCreateSshLoginMappingsDTO } from "./ssh-host-types";
|
import { TCreateSshLoginMappingsDTO } from "./ssh-host-types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,6 +16,7 @@ export const createSshLoginMappings = async ({
|
|||||||
loginMappings,
|
loginMappings,
|
||||||
sshHostLoginUserDAL,
|
sshHostLoginUserDAL,
|
||||||
sshHostLoginUserMappingDAL,
|
sshHostLoginUserMappingDAL,
|
||||||
|
groupDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectId,
|
projectId,
|
||||||
@@ -35,7 +37,7 @@ export const createSshLoginMappings = async ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
if (allowedPrincipals.usernames.length > 0) {
|
if (allowedPrincipals.usernames && allowedPrincipals.usernames.length > 0) {
|
||||||
const users = await userDAL.find(
|
const users = await userDAL.find(
|
||||||
{
|
{
|
||||||
$in: {
|
$in: {
|
||||||
@@ -74,6 +76,41 @@ export const createSshLoginMappings = async ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (allowedPrincipals.groups && allowedPrincipals.groups.length > 0) {
|
||||||
|
const projectGroups = await groupDAL.findGroupsByProjectId(projectId);
|
||||||
|
const groups = projectGroups.filter((g) => allowedPrincipals.groups?.includes(g.slug));
|
||||||
|
|
||||||
|
if (groups.length !== allowedPrincipals.groups?.length) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Invalid group slugs: ${allowedPrincipals.groups
|
||||||
|
.filter((g) => !projectGroups.some((pg) => pg.slug === g))
|
||||||
|
.join(", ")}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for await (const group of groups) {
|
||||||
|
// check that each group has access to the SSH project and have read access to hosts
|
||||||
|
const hasPermission = await permissionService.checkGroupProjectPermission({
|
||||||
|
groupId: group.id,
|
||||||
|
projectId,
|
||||||
|
checkPermissions: [ProjectPermissionSshHostActions.Read, ProjectPermissionSub.SshHosts]
|
||||||
|
});
|
||||||
|
if (!hasPermission) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Group ${group.slug} does not have access to the SSH project`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await sshHostLoginUserMappingDAL.insertMany(
|
||||||
|
groups.map((group) => ({
|
||||||
|
sshHostLoginUserId: sshHostLoginUser.id,
|
||||||
|
groupId: group.id
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,24 @@ export const sanitizedSshHost = SshHostsSchema.pick({
|
|||||||
|
|
||||||
export const loginMappingSchema = z.object({
|
export const loginMappingSchema = z.object({
|
||||||
loginUser: z.string().trim(),
|
loginUser: z.string().trim(),
|
||||||
allowedPrincipals: z.object({
|
allowedPrincipals: z
|
||||||
usernames: z.array(z.string().trim()).transform((usernames) => Array.from(new Set(usernames)))
|
.object({
|
||||||
})
|
usernames: z
|
||||||
|
.array(z.string().trim())
|
||||||
|
.transform((usernames) => Array.from(new Set(usernames)))
|
||||||
|
.optional(),
|
||||||
|
groups: z
|
||||||
|
.array(z.string().trim())
|
||||||
|
.transform((groups) => Array.from(new Set(groups)))
|
||||||
|
.optional()
|
||||||
|
})
|
||||||
|
.refine(
|
||||||
|
(data) => {
|
||||||
|
return (data.usernames && data.usernames.length > 0) || (data.groups && data.groups.length > 0);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: "At least one username or group must be provided",
|
||||||
|
path: ["allowedPrincipals"]
|
||||||
|
}
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
|
|
||||||
import { ActionProjectType, ProjectType } from "@app/db/schemas";
|
import { ActionProjectType, ProjectType } from "@app/db/schemas";
|
||||||
|
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { ProjectPermissionSshHostActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionSshHostActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { TSshCertificateAuthorityDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-dal";
|
import { TSshCertificateAuthorityDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-dal";
|
||||||
@@ -19,6 +20,7 @@ import { TProjectDALFactory } from "@app/services/project/project-dal";
|
|||||||
import { TProjectSshConfigDALFactory } from "@app/services/project/project-ssh-config-dal";
|
import { TProjectSshConfigDALFactory } from "@app/services/project/project-ssh-config-dal";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
|
import { TUserGroupMembershipDALFactory } from "../group/user-group-membership-dal";
|
||||||
import {
|
import {
|
||||||
convertActorToPrincipals,
|
convertActorToPrincipals,
|
||||||
createSshCert,
|
createSshCert,
|
||||||
@@ -39,12 +41,14 @@ import {
|
|||||||
|
|
||||||
type TSshHostServiceFactoryDep = {
|
type TSshHostServiceFactoryDep = {
|
||||||
userDAL: Pick<TUserDALFactory, "findById" | "find">;
|
userDAL: Pick<TUserDALFactory, "findById" | "find">;
|
||||||
|
groupDAL: Pick<TGroupDALFactory, "findGroupsByProjectId">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "find">;
|
projectDAL: Pick<TProjectDALFactory, "find">;
|
||||||
projectSshConfigDAL: Pick<TProjectSshConfigDALFactory, "findOne">;
|
projectSshConfigDAL: Pick<TProjectSshConfigDALFactory, "findOne">;
|
||||||
sshCertificateAuthorityDAL: Pick<TSshCertificateAuthorityDALFactory, "findOne">;
|
sshCertificateAuthorityDAL: Pick<TSshCertificateAuthorityDALFactory, "findOne">;
|
||||||
sshCertificateAuthoritySecretDAL: Pick<TSshCertificateAuthoritySecretDALFactory, "findOne">;
|
sshCertificateAuthoritySecretDAL: Pick<TSshCertificateAuthoritySecretDALFactory, "findOne">;
|
||||||
sshCertificateDAL: Pick<TSshCertificateDALFactory, "create" | "transaction">;
|
sshCertificateDAL: Pick<TSshCertificateDALFactory, "create" | "transaction">;
|
||||||
sshCertificateBodyDAL: Pick<TSshCertificateBodyDALFactory, "create">;
|
sshCertificateBodyDAL: Pick<TSshCertificateBodyDALFactory, "create">;
|
||||||
|
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "findGroupMembershipsByUserIdInOrg">;
|
||||||
sshHostDAL: Pick<
|
sshHostDAL: Pick<
|
||||||
TSshHostDALFactory,
|
TSshHostDALFactory,
|
||||||
| "transaction"
|
| "transaction"
|
||||||
@@ -58,7 +62,10 @@ type TSshHostServiceFactoryDep = {
|
|||||||
>;
|
>;
|
||||||
sshHostLoginUserDAL: TSshHostLoginUserDALFactory;
|
sshHostLoginUserDAL: TSshHostLoginUserDALFactory;
|
||||||
sshHostLoginUserMappingDAL: TSshHostLoginUserMappingDALFactory;
|
sshHostLoginUserMappingDAL: TSshHostLoginUserMappingDALFactory;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getUserProjectPermission">;
|
permissionService: Pick<
|
||||||
|
TPermissionServiceFactory,
|
||||||
|
"getProjectPermission" | "getUserProjectPermission" | "checkGroupProjectPermission"
|
||||||
|
>;
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -66,6 +73,8 @@ export type TSshHostServiceFactory = ReturnType<typeof sshHostServiceFactory>;
|
|||||||
|
|
||||||
export const sshHostServiceFactory = ({
|
export const sshHostServiceFactory = ({
|
||||||
userDAL,
|
userDAL,
|
||||||
|
userGroupMembershipDAL,
|
||||||
|
groupDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectSshConfigDAL,
|
projectSshConfigDAL,
|
||||||
sshCertificateAuthorityDAL,
|
sshCertificateAuthorityDAL,
|
||||||
@@ -208,6 +217,7 @@ export const sshHostServiceFactory = ({
|
|||||||
loginMappings,
|
loginMappings,
|
||||||
sshHostLoginUserDAL,
|
sshHostLoginUserDAL,
|
||||||
sshHostLoginUserMappingDAL,
|
sshHostLoginUserMappingDAL,
|
||||||
|
groupDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectId,
|
projectId,
|
||||||
@@ -278,6 +288,7 @@ export const sshHostServiceFactory = ({
|
|||||||
loginMappings,
|
loginMappings,
|
||||||
sshHostLoginUserDAL,
|
sshHostLoginUserDAL,
|
||||||
sshHostLoginUserMappingDAL,
|
sshHostLoginUserMappingDAL,
|
||||||
|
groupDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectId: host.projectId,
|
projectId: host.projectId,
|
||||||
@@ -324,7 +335,7 @@ export const sshHostServiceFactory = ({
|
|||||||
return host;
|
return host;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSshHost = async ({ sshHostId, actorId, actorAuthMethod, actor, actorOrgId }: TGetSshHostDTO) => {
|
const getSshHostById = async ({ sshHostId, actorId, actorAuthMethod, actor, actorOrgId }: TGetSshHostDTO) => {
|
||||||
const host = await sshHostDAL.findSshHostByIdWithLoginMappings(sshHostId);
|
const host = await sshHostDAL.findSshHostByIdWithLoginMappings(sshHostId);
|
||||||
if (!host) {
|
if (!host) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
@@ -387,10 +398,14 @@ export const sshHostServiceFactory = ({
|
|||||||
userDAL
|
userDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const userGroups = await userGroupMembershipDAL.findGroupMembershipsByUserIdInOrg(actorId, actorOrgId);
|
||||||
|
const userGroupSlugs = userGroups.map((g) => g.groupSlug);
|
||||||
|
|
||||||
const mapping = host.loginMappings.find(
|
const mapping = host.loginMappings.find(
|
||||||
(m) =>
|
(m) =>
|
||||||
m.loginUser === loginUser &&
|
m.loginUser === loginUser &&
|
||||||
m.allowedPrincipals.usernames.some((allowed) => internalPrincipals.includes(allowed))
|
(m.allowedPrincipals.usernames?.some((allowed) => internalPrincipals.includes(allowed)) ||
|
||||||
|
m.allowedPrincipals.groups?.some((allowed) => userGroupSlugs.includes(allowed)))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!mapping) {
|
if (!mapping) {
|
||||||
@@ -616,7 +631,7 @@ export const sshHostServiceFactory = ({
|
|||||||
createSshHost,
|
createSshHost,
|
||||||
updateSshHost,
|
updateSshHost,
|
||||||
deleteSshHost,
|
deleteSshHost,
|
||||||
getSshHost,
|
getSshHostById,
|
||||||
issueSshHostUserCert,
|
issueSshHostUserCert,
|
||||||
issueSshHostHostCert,
|
issueSshHostHostCert,
|
||||||
getSshHostUserCaPk,
|
getSshHostUserCaPk,
|
||||||
|
|||||||
@@ -7,12 +7,15 @@ import { TProjectPermission } from "@app/lib/types";
|
|||||||
import { ActorAuthMethod } from "@app/services/auth/auth-type";
|
import { ActorAuthMethod } from "@app/services/auth/auth-type";
|
||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
|
import { TGroupDALFactory } from "../group/group-dal";
|
||||||
|
|
||||||
export type TListSshHostsDTO = Omit<TProjectPermission, "projectId">;
|
export type TListSshHostsDTO = Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TLoginMapping = {
|
export type TLoginMapping = {
|
||||||
loginUser: string;
|
loginUser: string;
|
||||||
allowedPrincipals: {
|
allowedPrincipals: {
|
||||||
usernames: string[];
|
usernames?: string[];
|
||||||
|
groups?: string[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,7 +66,8 @@ 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">;
|
permissionService: Pick<TPermissionServiceFactory, "getUserProjectPermission" | "checkGroupProjectPermission">;
|
||||||
|
groupDAL: Pick<TGroupDALFactory, "findGroupsByProjectId">;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
actorAuthMethod: ActorAuthMethod;
|
actorAuthMethod: ActorAuthMethod;
|
||||||
actorOrgId: string;
|
actorOrgId: string;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Redis } from "ioredis";
|
import { Redis } from "ioredis";
|
||||||
|
|
||||||
import { pgAdvisoryLockHashText } from "@app/lib/crypto/hashtext";
|
import { pgAdvisoryLockHashText } from "@app/lib/crypto/hashtext";
|
||||||
|
import { applyJitter } from "@app/lib/dates";
|
||||||
|
import { delay as delayMs } from "@app/lib/delay";
|
||||||
import { Redlock, Settings } from "@app/lib/red-lock";
|
import { Redlock, Settings } from "@app/lib/red-lock";
|
||||||
|
|
||||||
export const PgSqlLock = {
|
export const PgSqlLock = {
|
||||||
@@ -48,6 +50,13 @@ export const KeyStoreTtls = {
|
|||||||
AccessTokenStatusUpdateInSeconds: 120
|
AccessTokenStatusUpdateInSeconds: 120
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type TDeleteItems = {
|
||||||
|
pattern: string;
|
||||||
|
batchSize?: number;
|
||||||
|
delay?: number;
|
||||||
|
jitter?: number;
|
||||||
|
};
|
||||||
|
|
||||||
type TWaitTillReady = {
|
type TWaitTillReady = {
|
||||||
key: string;
|
key: string;
|
||||||
waitingCb?: () => void;
|
waitingCb?: () => void;
|
||||||
@@ -75,6 +84,35 @@ export const keyStoreFactory = (redisUrl: string) => {
|
|||||||
|
|
||||||
const deleteItem = async (key: string) => redis.del(key);
|
const deleteItem = async (key: string) => redis.del(key);
|
||||||
|
|
||||||
|
const deleteItems = async ({ pattern, batchSize = 500, delay = 1500, jitter = 200 }: TDeleteItems) => {
|
||||||
|
let cursor = "0";
|
||||||
|
let totalDeleted = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// Await in loop is needed so that Redis is not overwhelmed
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const [nextCursor, keys] = await redis.scan(cursor, "MATCH", pattern, "COUNT", 1000); // Count should be 1000 - 5000 for prod loads
|
||||||
|
cursor = nextCursor;
|
||||||
|
|
||||||
|
for (let i = 0; i < keys.length; i += batchSize) {
|
||||||
|
const batch = keys.slice(i, i + batchSize);
|
||||||
|
const pipeline = redis.pipeline();
|
||||||
|
for (const key of batch) {
|
||||||
|
pipeline.unlink(key);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await pipeline.exec();
|
||||||
|
totalDeleted += batch.length;
|
||||||
|
console.log("BATCH DONE");
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await delayMs(Math.max(0, applyJitter(delay, jitter)));
|
||||||
|
}
|
||||||
|
} while (cursor !== "0");
|
||||||
|
|
||||||
|
return totalDeleted;
|
||||||
|
};
|
||||||
|
|
||||||
const incrementBy = async (key: string, value: number) => redis.incrby(key, value);
|
const incrementBy = async (key: string, value: number) => redis.incrby(key, value);
|
||||||
|
|
||||||
const setExpiry = async (key: string, expiryInSeconds: number) => redis.expire(key, expiryInSeconds);
|
const setExpiry = async (key: string, expiryInSeconds: number) => redis.expire(key, expiryInSeconds);
|
||||||
@@ -94,7 +132,7 @@ export const keyStoreFactory = (redisUrl: string) => {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
waitingCb?.();
|
waitingCb?.();
|
||||||
setTimeout(resolve, Math.max(0, delay + Math.floor((Math.random() * 2 - 1) * jitter)));
|
setTimeout(resolve, Math.max(0, applyJitter(delay, jitter)));
|
||||||
});
|
});
|
||||||
attempts += 1;
|
attempts += 1;
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
@@ -108,6 +146,7 @@ export const keyStoreFactory = (redisUrl: string) => {
|
|||||||
setExpiry,
|
setExpiry,
|
||||||
setItemWithExpiry,
|
setItemWithExpiry,
|
||||||
deleteItem,
|
deleteItem,
|
||||||
|
deleteItems,
|
||||||
incrementBy,
|
incrementBy,
|
||||||
acquireLock(resources: string[], duration: number, settings?: Partial<Settings>) {
|
acquireLock(resources: string[], duration: number, settings?: Partial<Settings>) {
|
||||||
return redisLock.acquire(resources, duration, settings);
|
return redisLock.acquire(resources, duration, settings);
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
import RE2 from "re2";
|
||||||
|
|
||||||
|
import { applyJitter } from "@app/lib/dates";
|
||||||
|
import { delay as delayMs } from "@app/lib/delay";
|
||||||
import { Lock } from "@app/lib/red-lock";
|
import { Lock } from "@app/lib/red-lock";
|
||||||
|
|
||||||
import { TKeyStoreFactory } from "./keystore";
|
import { TKeyStoreFactory } from "./keystore";
|
||||||
@@ -19,6 +23,27 @@ export const inMemoryKeyStore = (): TKeyStoreFactory => {
|
|||||||
delete store[key];
|
delete store[key];
|
||||||
return 1;
|
return 1;
|
||||||
},
|
},
|
||||||
|
deleteItems: async ({ pattern, batchSize = 500, delay = 1500, jitter = 200 }) => {
|
||||||
|
const regex = new RE2(`^${pattern.replace(/[-[\]/{}()+?.\\^$|]/g, "\\$&").replace(/\*/g, ".*")}$`);
|
||||||
|
let totalDeleted = 0;
|
||||||
|
const keys = Object.keys(store);
|
||||||
|
|
||||||
|
for (let i = 0; i < keys.length; i += batchSize) {
|
||||||
|
const batch = keys.slice(i, i + batchSize);
|
||||||
|
|
||||||
|
for (const key of batch) {
|
||||||
|
if (regex.test(key)) {
|
||||||
|
delete store[key];
|
||||||
|
totalDeleted += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await delayMs(Math.max(0, applyJitter(delay, jitter)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalDeleted;
|
||||||
|
},
|
||||||
getItem: async (key) => {
|
getItem: async (key) => {
|
||||||
const value = store[key];
|
const value = store[key];
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
|
|||||||
@@ -14,10 +14,12 @@ export enum ApiDocsTags {
|
|||||||
UniversalAuth = "Universal Auth",
|
UniversalAuth = "Universal Auth",
|
||||||
GcpAuth = "GCP Auth",
|
GcpAuth = "GCP Auth",
|
||||||
AwsAuth = "AWS Auth",
|
AwsAuth = "AWS Auth",
|
||||||
|
OciAuth = "OCI Auth",
|
||||||
AzureAuth = "Azure Auth",
|
AzureAuth = "Azure Auth",
|
||||||
KubernetesAuth = "Kubernetes Auth",
|
KubernetesAuth = "Kubernetes Auth",
|
||||||
JwtAuth = "JWT Auth",
|
JwtAuth = "JWT Auth",
|
||||||
OidcAuth = "OIDC Auth",
|
OidcAuth = "OIDC Auth",
|
||||||
|
LdapAuth = "LDAP Auth",
|
||||||
Groups = "Groups",
|
Groups = "Groups",
|
||||||
Organizations = "Organizations",
|
Organizations = "Organizations",
|
||||||
Projects = "Projects",
|
Projects = "Projects",
|
||||||
@@ -45,6 +47,7 @@ export enum ApiDocsTags {
|
|||||||
PkiCertificateTemplates = "PKI Certificate Templates",
|
PkiCertificateTemplates = "PKI Certificate Templates",
|
||||||
PkiCertificateCollections = "PKI Certificate Collections",
|
PkiCertificateCollections = "PKI Certificate Collections",
|
||||||
PkiAlerting = "PKI Alerting",
|
PkiAlerting = "PKI Alerting",
|
||||||
|
PkiSubscribers = "PKI Subscribers",
|
||||||
SshCertificates = "SSH Certificates",
|
SshCertificates = "SSH Certificates",
|
||||||
SshCertificateAuthorities = "SSH Certificate Authorities",
|
SshCertificateAuthorities = "SSH Certificate Authorities",
|
||||||
SshCertificateTemplates = "SSH Certificate Templates",
|
SshCertificateTemplates = "SSH Certificate Templates",
|
||||||
@@ -184,6 +187,49 @@ export const UNIVERSAL_AUTH = {
|
|||||||
}
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const LDAP_AUTH = {
|
||||||
|
LOGIN: {
|
||||||
|
identityId: "The ID of the identity to login.",
|
||||||
|
username: "The username of the LDAP user to login.",
|
||||||
|
password: "The password of the LDAP user to login."
|
||||||
|
},
|
||||||
|
ATTACH: {
|
||||||
|
identityId: "The ID of the identity to attach the configuration onto.",
|
||||||
|
url: "The URL of the LDAP server.",
|
||||||
|
allowedFields:
|
||||||
|
"The comma-separated array of key/value pairs of required fields that the LDAP entry must have in order to authenticate.",
|
||||||
|
searchBase: "The base DN to search for the LDAP user.",
|
||||||
|
searchFilter: "The filter to use to search for the LDAP user.",
|
||||||
|
bindDN: "The DN of the user to bind to the LDAP server.",
|
||||||
|
bindPass: "The password of the user to bind to the LDAP server.",
|
||||||
|
ldapCaCertificate: "The PEM-encoded CA certificate for the LDAP server.",
|
||||||
|
accessTokenTTL: "The lifetime for an access token in seconds.",
|
||||||
|
accessTokenMaxTTL: "The maximum lifetime for an access token in seconds.",
|
||||||
|
accessTokenNumUsesLimit: "The maximum number of times that an access token can be used.",
|
||||||
|
accessTokenTrustedIps: "The IPs or CIDR ranges that access tokens can be used from."
|
||||||
|
},
|
||||||
|
UPDATE: {
|
||||||
|
identityId: "The ID of the identity to update the configuration for.",
|
||||||
|
url: "The new URL of the LDAP server.",
|
||||||
|
allowedFields: "The comma-separated list of allowed fields to return from the LDAP user.",
|
||||||
|
searchBase: "The new base DN to search for the LDAP user.",
|
||||||
|
searchFilter: "The new filter to use to search for the LDAP user.",
|
||||||
|
bindDN: "The new DN of the user to bind to the LDAP server.",
|
||||||
|
bindPass: "The new password of the user to bind to the LDAP server.",
|
||||||
|
ldapCaCertificate: "The new PEM-encoded CA certificate for the LDAP server.",
|
||||||
|
accessTokenTTL: "The new lifetime for an access token in seconds.",
|
||||||
|
accessTokenMaxTTL: "The new maximum lifetime for an access token in seconds.",
|
||||||
|
accessTokenNumUsesLimit: "The new maximum number of times that an access token can be used.",
|
||||||
|
accessTokenTrustedIps: "The new IPs or CIDR ranges that access tokens can be used from."
|
||||||
|
},
|
||||||
|
RETRIEVE: {
|
||||||
|
identityId: "The ID of the identity to retrieve the configuration for."
|
||||||
|
},
|
||||||
|
REVOKE: {
|
||||||
|
identityId: "The ID of the identity to revoke the configuration for."
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const AWS_AUTH = {
|
export const AWS_AUTH = {
|
||||||
LOGIN: {
|
LOGIN: {
|
||||||
identityId: "The ID of the identity to login.",
|
identityId: "The ID of the identity to login.",
|
||||||
@@ -226,6 +272,40 @@ export const AWS_AUTH = {
|
|||||||
}
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const OCI_AUTH = {
|
||||||
|
LOGIN: {
|
||||||
|
identityId: "The ID of the identity to login.",
|
||||||
|
userOcid: "The OCID of the user attempting login.",
|
||||||
|
headers: "The headers of the signed request."
|
||||||
|
},
|
||||||
|
ATTACH: {
|
||||||
|
identityId: "The ID of the identity to attach the configuration onto.",
|
||||||
|
tenancyOcid: "The OCID of your tenancy.",
|
||||||
|
allowedUsernames:
|
||||||
|
"The comma-separated list of trusted OCI account usernames that are allowed to authenticate with Infisical.",
|
||||||
|
accessTokenTTL: "The lifetime for an access token in seconds.",
|
||||||
|
accessTokenMaxTTL: "The maximum lifetime for an access token in seconds.",
|
||||||
|
accessTokenNumUsesLimit: "The maximum number of times that an access token can be used.",
|
||||||
|
accessTokenTrustedIps: "The IPs or CIDR ranges that access tokens can be used from."
|
||||||
|
},
|
||||||
|
UPDATE: {
|
||||||
|
identityId: "The ID of the identity to update the auth method for.",
|
||||||
|
tenancyOcid: "The OCID of your tenancy.",
|
||||||
|
allowedUsernames:
|
||||||
|
"The comma-separated list of trusted OCI account usernames that are allowed to authenticate with Infisical.",
|
||||||
|
accessTokenTTL: "The new lifetime for an access token in seconds.",
|
||||||
|
accessTokenMaxTTL: "The new maximum lifetime for an access token in seconds.",
|
||||||
|
accessTokenNumUsesLimit: "The new maximum number of times that an access token can be used.",
|
||||||
|
accessTokenTrustedIps: "The new IPs or CIDR ranges that access tokens can be used from."
|
||||||
|
},
|
||||||
|
RETRIEVE: {
|
||||||
|
identityId: "The ID of the identity to retrieve the auth method for."
|
||||||
|
},
|
||||||
|
REVOKE: {
|
||||||
|
identityId: "The ID of the identity to revoke the auth method for."
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const AZURE_AUTH = {
|
export const AZURE_AUTH = {
|
||||||
LOGIN: {
|
LOGIN: {
|
||||||
identityId: "The ID of the identity to login."
|
identityId: "The ID of the identity to login."
|
||||||
@@ -313,6 +393,7 @@ export const KUBERNETES_AUTH = {
|
|||||||
allowedNames: "The comma-separated list of trusted service account names that can authenticate with Infisical.",
|
allowedNames: "The comma-separated list of trusted service account names that can authenticate with Infisical.",
|
||||||
allowedAudience:
|
allowedAudience:
|
||||||
"The optional audience claim that the service account JWT token must have to authenticate with Infisical.",
|
"The optional audience claim that the service account JWT token must have to authenticate with Infisical.",
|
||||||
|
gatewayId: "The ID of the gateway to use when performing kubernetes API requests.",
|
||||||
accessTokenTrustedIps: "The IPs or CIDR ranges that access tokens can be used from.",
|
accessTokenTrustedIps: "The IPs or CIDR ranges that access tokens can be used from.",
|
||||||
accessTokenTTL: "The lifetime for an access token in seconds.",
|
accessTokenTTL: "The lifetime for an access token in seconds.",
|
||||||
accessTokenMaxTTL: "The maximum lifetime for an access token in seconds.",
|
accessTokenMaxTTL: "The maximum lifetime for an access token in seconds.",
|
||||||
@@ -329,6 +410,7 @@ export const KUBERNETES_AUTH = {
|
|||||||
allowedNames: "The new comma-separated list of trusted service account names that can authenticate with Infisical.",
|
allowedNames: "The new comma-separated list of trusted service account names that can authenticate with Infisical.",
|
||||||
allowedAudience:
|
allowedAudience:
|
||||||
"The new optional audience claim that the service account JWT token must have to authenticate with Infisical.",
|
"The new optional audience claim that the service account JWT token must have to authenticate with Infisical.",
|
||||||
|
gatewayId: "The ID of the gateway to use when performing kubernetes API requests.",
|
||||||
accessTokenTrustedIps: "The new IPs or CIDR ranges that access tokens can be used from.",
|
accessTokenTrustedIps: "The new IPs or CIDR ranges that access tokens can be used from.",
|
||||||
accessTokenTTL: "The new lifetime for an acccess token in seconds.",
|
accessTokenTTL: "The new lifetime for an acccess token in seconds.",
|
||||||
accessTokenMaxTTL: "The new maximum lifetime for an acccess token in seconds.",
|
accessTokenMaxTTL: "The new maximum lifetime for an acccess token in seconds.",
|
||||||
@@ -526,7 +608,8 @@ export const PROJECTS = {
|
|||||||
projectDescription: "An optional description label for the project.",
|
projectDescription: "An optional description label for the project.",
|
||||||
autoCapitalization: "Disable or enable auto-capitalization for the project.",
|
autoCapitalization: "Disable or enable auto-capitalization for the project.",
|
||||||
slug: "An optional slug for the project. (must be unique within the organization)",
|
slug: "An optional slug for the project. (must be unique within the organization)",
|
||||||
hasDeleteProtection: "Enable or disable delete protection for the project."
|
hasDeleteProtection: "Enable or disable delete protection for the project.",
|
||||||
|
secretSharing: "Enable or disable secret sharing for the project."
|
||||||
},
|
},
|
||||||
GET_KEY: {
|
GET_KEY: {
|
||||||
workspaceId: "The ID of the project to get the key from."
|
workspaceId: "The ID of the project to get the key from."
|
||||||
@@ -595,6 +678,9 @@ export const PROJECTS = {
|
|||||||
commonName: "The common name of the certificate to filter by.",
|
commonName: "The common name of the certificate to filter by.",
|
||||||
offset: "The offset to start from. If you enter 10, it will start from the 10th certificate.",
|
offset: "The offset to start from. If you enter 10, it will start from the 10th certificate.",
|
||||||
limit: "The number of certificates to return."
|
limit: "The number of certificates to return."
|
||||||
|
},
|
||||||
|
LIST_PKI_SUBSCRIBERS: {
|
||||||
|
projectId: "The ID of the project to list PKI subscribers for."
|
||||||
}
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@@ -1434,7 +1520,7 @@ export const SSH_HOSTS = {
|
|||||||
loginUser: "A login user on the remote machine (e.g. 'ec2-user', 'deploy', 'admin')",
|
loginUser: "A login user on the remote machine (e.g. 'ec2-user', 'deploy', 'admin')",
|
||||||
allowedPrincipals: "A list of allowed principals that can log in as the login user.",
|
allowedPrincipals: "A list of allowed principals that can log in as the login user.",
|
||||||
loginMappings:
|
loginMappings:
|
||||||
"A list of login mappings for the SSH host. Each login mapping contains a login user and a list of corresponding allowed principals being usernames of users in the Infisical SSH project.",
|
"A list of login mappings for the SSH host. Each login mapping contains a login user and a list of corresponding allowed principals being usernames of users or groups slugs in the Infisical SSH project.",
|
||||||
userSshCaId:
|
userSshCaId:
|
||||||
"The ID of the SSH CA to use for user certificates. If not specified, the default user SSH CA will be used if it exists.",
|
"The ID of the SSH CA to use for user certificates. If not specified, the default user SSH CA will be used if it exists.",
|
||||||
hostSshCaId:
|
hostSshCaId:
|
||||||
@@ -1449,7 +1535,7 @@ export const SSH_HOSTS = {
|
|||||||
loginUser: "A login user on the remote machine (e.g. 'ec2-user', 'deploy', 'admin')",
|
loginUser: "A login user on the remote machine (e.g. 'ec2-user', 'deploy', 'admin')",
|
||||||
allowedPrincipals: "A list of allowed principals that can log in as the login user.",
|
allowedPrincipals: "A list of allowed principals that can log in as the login user.",
|
||||||
loginMappings:
|
loginMappings:
|
||||||
"A list of login mappings for the SSH host. Each login mapping contains a login user and a list of corresponding allowed principals being usernames of users in the Infisical SSH project."
|
"A list of login mappings for the SSH host. Each login mapping contains a login user and a list of corresponding allowed principals being usernames of users or groups slugs in the Infisical SSH project."
|
||||||
},
|
},
|
||||||
DELETE: {
|
DELETE: {
|
||||||
sshHostId: "The ID of the SSH host to delete."
|
sshHostId: "The ID of the SSH host to delete."
|
||||||
@@ -1619,7 +1705,8 @@ export const CERTIFICATES = {
|
|||||||
serialNumber: "The serial number of the certificate to get the certificate body and certificate chain for.",
|
serialNumber: "The serial number of the certificate to get the certificate body and certificate chain for.",
|
||||||
certificate: "The certificate body of the certificate.",
|
certificate: "The certificate body of the certificate.",
|
||||||
certificateChain: "The certificate chain of the certificate.",
|
certificateChain: "The certificate chain of the certificate.",
|
||||||
serialNumberRes: "The serial number of the certificate."
|
serialNumberRes: "The serial number of the certificate.",
|
||||||
|
privateKey: "The private key of the certificate."
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1686,6 +1773,67 @@ export const ALERTS = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const PKI_SUBSCRIBERS = {
|
||||||
|
GET: {
|
||||||
|
subscriberName: "The name of the PKI subscriber to get.",
|
||||||
|
projectId: "The ID of the project to get the PKI subscriber for."
|
||||||
|
},
|
||||||
|
CREATE: {
|
||||||
|
projectId: "The ID of the project to create the PKI subscriber in.",
|
||||||
|
caId: "The ID of the CA that will issue certificates for the PKI subscriber.",
|
||||||
|
name: "The name of the PKI subscriber.",
|
||||||
|
commonName: "The common name (CN) to be used on certificates issued for this subscriber.",
|
||||||
|
status: "The status of the PKI subscriber. This can be one of active or disabled.",
|
||||||
|
ttl: "The time to live for the certificates issued for this subscriber such as 1m, 1h, 1d, 1y, ...",
|
||||||
|
subjectAlternativeNames:
|
||||||
|
"A list of Subject Alternative Names (SANs) to be used on certificates issued for this subscriber; these can be host names or email addresses.",
|
||||||
|
keyUsages: "The key usage extension to be used on certificates issued for this subscriber.",
|
||||||
|
extendedKeyUsages: "The extended key usage extension to be used on certificates issued for this subscriber."
|
||||||
|
},
|
||||||
|
UPDATE: {
|
||||||
|
projectId: "The ID of the project to update the PKI subscriber in.",
|
||||||
|
subscriberName: "The name of the PKI subscriber to update.",
|
||||||
|
caId: "The ID of the CA that will issue certificates for the PKI subscriber to update to.",
|
||||||
|
name: "The name of the PKI subscriber to update to.",
|
||||||
|
commonName: "The common name (CN) to be used on certificates issued for this subscriber to update to.",
|
||||||
|
status: "The status of the PKI subscriber to update to. This can be one of active or disabled.",
|
||||||
|
ttl: "The time to live for the certificates issued for this subscriber such as 1m, 1h, 1d, 1y, ...",
|
||||||
|
subjectAlternativeNames:
|
||||||
|
"A comma-delimited list of Subject Alternative Names (SANs) to be used on certificates issued for this subscriber; these can be host names or email addresses.",
|
||||||
|
keyUsages: "The key usage extension to be used on certificates issued for this subscriber to update to.",
|
||||||
|
extendedKeyUsages:
|
||||||
|
"The extended key usage extension to be used on certificates issued for this subscriber to update to."
|
||||||
|
},
|
||||||
|
DELETE: {
|
||||||
|
subscriberName: "The name of the PKI subscriber to delete.",
|
||||||
|
projectId: "The ID of the project of the PKI subscriber to delete."
|
||||||
|
},
|
||||||
|
ISSUE_CERT: {
|
||||||
|
subscriberName: "The name of the PKI subscriber to issue the certificate for.",
|
||||||
|
projectId: "The ID of the project of the PKI subscriber to issue the certificate for.",
|
||||||
|
certificate: "The issued certificate.",
|
||||||
|
issuingCaCertificate: "The certificate of the issuing CA.",
|
||||||
|
certificateChain: "The certificate chain of the issued certificate.",
|
||||||
|
privateKey: "The private key of the issued certificate.",
|
||||||
|
serialNumber: "The serial number of the issued certificate."
|
||||||
|
},
|
||||||
|
SIGN_CERT: {
|
||||||
|
subscriberName: "The name of the PKI subscriber to sign the certificate for.",
|
||||||
|
projectId: "The ID of the project of the PKI subscriber to sign the certificate for.",
|
||||||
|
csr: "The CSR to be used to sign the certificate.",
|
||||||
|
certificate: "The signed certificate.",
|
||||||
|
issuingCaCertificate: "The certificate of the issuing CA.",
|
||||||
|
certificateChain: "The certificate chain of the signed certificate.",
|
||||||
|
serialNumber: "The serial number of the signed certificate."
|
||||||
|
},
|
||||||
|
LIST_CERTS: {
|
||||||
|
subscriberName: "The name of the PKI subscriber to list the certificates for.",
|
||||||
|
projectId: "The ID of the project of the PKI subscriber to list the certificates for.",
|
||||||
|
offset: "The offset to start from.",
|
||||||
|
limit: "The number of certificates to return."
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const PKI_COLLECTIONS = {
|
export const PKI_COLLECTIONS = {
|
||||||
CREATE: {
|
CREATE: {
|
||||||
projectId: "The ID of the project to create the PKI collection in.",
|
projectId: "The ID of the project to create the PKI collection in.",
|
||||||
@@ -1821,8 +1969,12 @@ export const KMS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ProjectTemplates = {
|
export const ProjectTemplates = {
|
||||||
|
LIST: {
|
||||||
|
type: "The type of project template to list."
|
||||||
|
},
|
||||||
CREATE: {
|
CREATE: {
|
||||||
name: "The name of the project template to be created. Must be slug-friendly.",
|
name: "The name of the project template to be created. Must be slug-friendly.",
|
||||||
|
type: "The type of project template to be created.",
|
||||||
description: "An optional description of the project template.",
|
description: "An optional description of the project template.",
|
||||||
roles: "The roles to be created when the template is applied to a project.",
|
roles: "The roles to be created when the template is applied to a project.",
|
||||||
environments: "The environments to be created when the template is applied to a project."
|
environments: "The environments to be created when the template is applied to a project."
|
||||||
@@ -1925,6 +2077,13 @@ export const AppConnections = {
|
|||||||
AZURE_CLIENT_SECRETS: {
|
AZURE_CLIENT_SECRETS: {
|
||||||
code: "The OAuth code to use to connect with Azure Client Secrets.",
|
code: "The OAuth code to use to connect with Azure Client Secrets.",
|
||||||
tenantId: "The Tenant ID to use to connect with Azure Client Secrets."
|
tenantId: "The Tenant ID to use to connect with Azure Client Secrets."
|
||||||
|
},
|
||||||
|
OCI: {
|
||||||
|
userOcid: "The OCID (Oracle Cloud Identifier) of the user making the request.",
|
||||||
|
tenancyOcid: "The OCID (Oracle Cloud Identifier) of the tenancy in Oracle Cloud Infrastructure.",
|
||||||
|
region: "The region identifier in Oracle Cloud Infrastructure where the vault is located.",
|
||||||
|
fingerprint: "The fingerprint of the public key uploaded to the user's API keys.",
|
||||||
|
privateKey: "The private key content in PEM format used to sign API requests."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1988,6 +2147,7 @@ export const SecretSyncs = {
|
|||||||
const destinationName = SECRET_SYNC_NAME_MAP[destination];
|
const destinationName = SECRET_SYNC_NAME_MAP[destination];
|
||||||
return {
|
return {
|
||||||
initialSyncBehavior: `Specify how Infisical should resolve the initial sync to the ${destinationName} destination.`,
|
initialSyncBehavior: `Specify how Infisical should resolve the initial sync to the ${destinationName} destination.`,
|
||||||
|
keySchema: `Specify the format to use for structuring secret keys in the ${destinationName} destination.`,
|
||||||
disableSecretDeletion: `Enable this flag to prevent removal of secrets from the ${destinationName} destination when syncing.`
|
disableSecretDeletion: `Enable this flag to prevent removal of secrets from the ${destinationName} destination when syncing.`
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -2072,6 +2232,11 @@ export const SecretSyncs = {
|
|||||||
TEAMCITY: {
|
TEAMCITY: {
|
||||||
project: "The TeamCity project to sync secrets to.",
|
project: "The TeamCity project to sync secrets to.",
|
||||||
buildConfig: "The TeamCity build configuration to sync secrets to."
|
buildConfig: "The TeamCity build configuration to sync secrets to."
|
||||||
|
},
|
||||||
|
OCI_VAULT: {
|
||||||
|
compartmentOcid: "The OCID (Oracle Cloud Identifier) of the compartment where the vault is located.",
|
||||||
|
vaultOcid: "The OCID (Oracle Cloud Identifier) of the vault to sync secrets to.",
|
||||||
|
keyOcid: "The OCID (Oracle Cloud Identifier) of the encryption key to use when creating secrets in the vault."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ const envSchema = z
|
|||||||
SECRET_SCANNING_GIT_APP_ID: zpStr(z.string().optional()),
|
SECRET_SCANNING_GIT_APP_ID: zpStr(z.string().optional()),
|
||||||
SECRET_SCANNING_PRIVATE_KEY: zpStr(z.string().optional()),
|
SECRET_SCANNING_PRIVATE_KEY: zpStr(z.string().optional()),
|
||||||
SECRET_SCANNING_ORG_WHITELIST: zpStr(z.string().optional()),
|
SECRET_SCANNING_ORG_WHITELIST: zpStr(z.string().optional()),
|
||||||
|
SECRET_SCANNING_GIT_APP_SLUG: zpStr(z.string().default("infisical-radar")),
|
||||||
// LICENSE
|
// LICENSE
|
||||||
LICENSE_SERVER_URL: zpStr(z.string().optional().default("https://portal.infisical.com")),
|
LICENSE_SERVER_URL: zpStr(z.string().optional().default("https://portal.infisical.com")),
|
||||||
LICENSE_SERVER_KEY: zpStr(z.string().optional()),
|
LICENSE_SERVER_KEY: zpStr(z.string().optional()),
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export const delay = (ms: number) =>
|
||||||
|
new Promise<void>((resolve) => {
|
||||||
|
setTimeout(resolve, ms);
|
||||||
|
});
|
||||||
@@ -174,6 +174,8 @@ const setupProxyServer = async ({
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const server = net.createServer();
|
const server = net.createServer();
|
||||||
|
|
||||||
|
let streamClosed = false;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
server.on("connection", async (clientConn) => {
|
server.on("connection", async (clientConn) => {
|
||||||
try {
|
try {
|
||||||
@@ -202,9 +204,15 @@ const setupProxyServer = async ({
|
|||||||
|
|
||||||
// Handle client connection close
|
// Handle client connection close
|
||||||
clientConn.on("end", () => {
|
clientConn.on("end", () => {
|
||||||
writer.close().catch((err) => {
|
if (!streamClosed) {
|
||||||
logger.error(err);
|
try {
|
||||||
});
|
writer.close().catch((err) => {
|
||||||
|
logger.debug(err, "Error closing writer (already closed)");
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.debug(error, "Error in writer close");
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
clientConn.on("error", (clientConnErr) => {
|
clientConn.on("error", (clientConnErr) => {
|
||||||
@@ -249,14 +257,29 @@ const setupProxyServer = async ({
|
|||||||
setupCopy();
|
setupCopy();
|
||||||
// Handle connection closure
|
// Handle connection closure
|
||||||
clientConn.on("close", () => {
|
clientConn.on("close", () => {
|
||||||
stream.destroy().catch((err) => {
|
if (!streamClosed) {
|
||||||
proxyErrorMsg.push((err as Error)?.message);
|
streamClosed = true;
|
||||||
});
|
stream.destroy().catch((err) => {
|
||||||
|
logger.debug(err, "Stream already destroyed during close event");
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const cleanup = async () => {
|
const cleanup = async () => {
|
||||||
clientConn?.destroy();
|
try {
|
||||||
await stream.destroy();
|
clientConn?.destroy();
|
||||||
|
} catch (err) {
|
||||||
|
logger.debug(err, "Error destroying client connection");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!streamClosed) {
|
||||||
|
streamClosed = true;
|
||||||
|
try {
|
||||||
|
await stream.destroy();
|
||||||
|
} catch (err) {
|
||||||
|
logger.debug(err, "Error destroying stream (might be already closed)");
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
clientConn.on("error", (clientConnErr) => {
|
clientConn.on("error", (clientConnErr) => {
|
||||||
@@ -301,8 +324,17 @@ const setupProxyServer = async ({
|
|||||||
server,
|
server,
|
||||||
port: address.port,
|
port: address.port,
|
||||||
cleanup: async () => {
|
cleanup: async () => {
|
||||||
server.close();
|
try {
|
||||||
await quicClient?.destroy();
|
server.close();
|
||||||
|
} catch (err) {
|
||||||
|
logger.debug(err, "Error closing server");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await quicClient?.destroy();
|
||||||
|
} catch (err) {
|
||||||
|
logger.debug(err, "Error destroying QUIC client");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getProxyError: () => proxyErrorMsg.join(",")
|
getProxyError: () => proxyErrorMsg.join(",")
|
||||||
});
|
});
|
||||||
@@ -320,10 +352,10 @@ interface ProxyOptions {
|
|||||||
orgId: string;
|
orgId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const withGatewayProxy = async (
|
export const withGatewayProxy = async <T>(
|
||||||
callback: (port: number) => Promise<void>,
|
callback: (port: number) => Promise<T>,
|
||||||
options: ProxyOptions
|
options: ProxyOptions
|
||||||
): Promise<void> => {
|
): Promise<T> => {
|
||||||
const { relayHost, relayPort, targetHost, targetPort, tlsOptions, identityId, orgId } = options;
|
const { relayHost, relayPort, targetHost, targetPort, tlsOptions, identityId, orgId } = options;
|
||||||
|
|
||||||
// Setup the proxy server
|
// Setup the proxy server
|
||||||
@@ -339,7 +371,7 @@ export const withGatewayProxy = async (
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Execute the callback with the allocated port
|
// Execute the callback with the allocated port
|
||||||
await callback(port);
|
return await callback(port);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const proxyErrorMessage = getProxyError();
|
const proxyErrorMessage = getProxyError();
|
||||||
if (proxyErrorMessage) {
|
if (proxyErrorMessage) {
|
||||||
|
|||||||
@@ -32,13 +32,13 @@ export const buildFindFilter =
|
|||||||
<R extends object = object>(
|
<R extends object = object>(
|
||||||
{ $in, $notNull, $search, $complex, ...filter }: TFindFilter<R>,
|
{ $in, $notNull, $search, $complex, ...filter }: TFindFilter<R>,
|
||||||
tableName?: TableName,
|
tableName?: TableName,
|
||||||
excludeKeys?: Array<keyof R>
|
excludeKeys?: string[]
|
||||||
) =>
|
) =>
|
||||||
(bd: Knex.QueryBuilder<R, R>) => {
|
(bd: Knex.QueryBuilder<R, R>) => {
|
||||||
const processedFilter = tableName
|
const processedFilter = tableName
|
||||||
? Object.fromEntries(
|
? Object.fromEntries(
|
||||||
Object.entries(filter)
|
Object.entries(filter)
|
||||||
.filter(([key]) => !excludeKeys || !excludeKeys.includes(key as keyof R))
|
.filter(([key]) => !excludeKeys || !excludeKeys.includes(key))
|
||||||
.map(([key, value]) => [`${tableName}.${key}`, value])
|
.map(([key, value]) => [`${tableName}.${key}`, value])
|
||||||
)
|
)
|
||||||
: filter;
|
: filter;
|
||||||
|
|||||||
@@ -84,7 +84,9 @@ const redactedKeys = [
|
|||||||
"secrets",
|
"secrets",
|
||||||
"key",
|
"key",
|
||||||
"password",
|
"password",
|
||||||
"config"
|
"config",
|
||||||
|
"bindPass",
|
||||||
|
"bindDN"
|
||||||
];
|
];
|
||||||
|
|
||||||
const UNKNOWN_REQUEST_ID = "UNKNOWN_REQUEST_ID";
|
const UNKNOWN_REQUEST_ID = "UNKNOWN_REQUEST_ID";
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
TQueueSecretSyncSyncSecretsByIdDTO,
|
TQueueSecretSyncSyncSecretsByIdDTO,
|
||||||
TQueueSendSecretSyncActionFailedNotificationsDTO
|
TQueueSendSecretSyncActionFailedNotificationsDTO
|
||||||
} from "@app/services/secret-sync/secret-sync-types";
|
} from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
import { CacheType } from "@app/services/super-admin/super-admin-types";
|
||||||
import { TWebhookPayloads } from "@app/services/webhook/webhook-types";
|
import { TWebhookPayloads } from "@app/services/webhook/webhook-types";
|
||||||
|
|
||||||
export enum QueueName {
|
export enum QueueName {
|
||||||
@@ -50,7 +51,8 @@ export enum QueueName {
|
|||||||
ImportSecretsFromExternalSource = "import-secrets-from-external-source",
|
ImportSecretsFromExternalSource = "import-secrets-from-external-source",
|
||||||
AppConnectionSecretSync = "app-connection-secret-sync",
|
AppConnectionSecretSync = "app-connection-secret-sync",
|
||||||
SecretRotationV2 = "secret-rotation-v2",
|
SecretRotationV2 = "secret-rotation-v2",
|
||||||
FolderTreeCheckpoint = "folder-tree-checkpoint"
|
FolderTreeCheckpoint = "folder-tree-checkpoint",
|
||||||
|
InvalidateCache = "invalidate-cache"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum QueueJobs {
|
export enum QueueJobs {
|
||||||
@@ -83,7 +85,8 @@ export enum QueueJobs {
|
|||||||
SecretRotationV2QueueRotations = "secret-rotation-v2-queue-rotations",
|
SecretRotationV2QueueRotations = "secret-rotation-v2-queue-rotations",
|
||||||
SecretRotationV2RotateSecrets = "secret-rotation-v2-rotate-secrets",
|
SecretRotationV2RotateSecrets = "secret-rotation-v2-rotate-secrets",
|
||||||
SecretRotationV2SendNotification = "secret-rotation-v2-send-notification",
|
SecretRotationV2SendNotification = "secret-rotation-v2-send-notification",
|
||||||
CreateFolderTreeCheckpoint = "create-folder-tree-checkpoint"
|
CreateFolderTreeCheckpoint = "create-folder-tree-checkpoint",
|
||||||
|
InvalidateCache = "invalidate-cache"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TQueueJobTypes = {
|
export type TQueueJobTypes = {
|
||||||
@@ -242,6 +245,14 @@ export type TQueueJobTypes = {
|
|||||||
name: QueueJobs.SecretRotationV2SendNotification;
|
name: QueueJobs.SecretRotationV2SendNotification;
|
||||||
payload: TSecretRotationSendNotificationJobPayload;
|
payload: TSecretRotationSendNotificationJobPayload;
|
||||||
};
|
};
|
||||||
|
[QueueName.InvalidateCache]: {
|
||||||
|
name: QueueJobs.InvalidateCache;
|
||||||
|
payload: {
|
||||||
|
data: {
|
||||||
|
type: CacheType;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TQueueServiceFactory = ReturnType<typeof queueServiceFactory>;
|
export type TQueueServiceFactory = ReturnType<typeof queueServiceFactory>;
|
||||||
|
|||||||
@@ -100,3 +100,18 @@ export const publicSshCaLimit: RateLimitOptions = {
|
|||||||
max: 30, // conservative default
|
max: 30, // conservative default
|
||||||
keyGenerator: (req) => req.realIp
|
keyGenerator: (req) => req.realIp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const invalidateCacheLimit: RateLimitOptions = {
|
||||||
|
timeWindow: 60 * 1000,
|
||||||
|
hook: "preValidation",
|
||||||
|
max: 2,
|
||||||
|
keyGenerator: (req) => req.realIp
|
||||||
|
};
|
||||||
|
|
||||||
|
// Makes spamming "request access" harder, preventing email DDoS
|
||||||
|
export const requestAccessLimit: RateLimitOptions = {
|
||||||
|
timeWindow: 60 * 1000,
|
||||||
|
hook: "preValidation",
|
||||||
|
max: 10,
|
||||||
|
keyGenerator: (req) => req.realIp
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { FastifyReply } from "fastify";
|
||||||
|
|
||||||
|
export const addNoCacheHeaders = (reply: FastifyReply) => {
|
||||||
|
void reply.header("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
|
||||||
|
void reply.header("Pragma", "no-cache");
|
||||||
|
void reply.header("Expires", "0");
|
||||||
|
void reply.header("Surrogate-Control", "no-store");
|
||||||
|
};
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
import type { FastifySchema, FastifySchemaCompiler, FastifyTypeProvider } from "fastify";
|
import type { FastifySchema, FastifySchemaCompiler, FastifyTypeProvider } from "fastify";
|
||||||
import type { FastifySerializerCompiler } from "fastify/types/schema";
|
import type { FastifySerializerCompiler } from "fastify/types/schema";
|
||||||
import type { z, ZodAny, ZodTypeAny } from "zod";
|
import type { z, ZodAny, ZodTypeAny } from "zod";
|
||||||
import { zodToJsonSchema } from "zod-to-json-schema";
|
import { PostProcessCallback, zodToJsonSchema } from "zod-to-json-schema";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
type FreeformRecord = Record<string, any>;
|
type FreeformRecord = Record<string, any>;
|
||||||
@@ -28,9 +28,25 @@ interface Schema extends FastifySchema {
|
|||||||
hide?: boolean;
|
hide?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Credit: https://github.com/StefanTerdell/zod-to-json-schema
|
||||||
|
const jsonDescription: PostProcessCallback = (jsonSchema, def) => {
|
||||||
|
if (def.description) {
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
...jsonSchema,
|
||||||
|
description: undefined,
|
||||||
|
...JSON.parse(def.description)
|
||||||
|
};
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonSchema;
|
||||||
|
};
|
||||||
|
|
||||||
const zodToJsonSchemaOptions = {
|
const zodToJsonSchemaOptions = {
|
||||||
target: "openApi3",
|
target: "openApi3",
|
||||||
$refStrategy: "none"
|
$refStrategy: "none",
|
||||||
|
postProcess: jsonDescription
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ export const registerServeUI = async (
|
|||||||
reply.callNotFound();
|
reply.callNotFound();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return reply.sendFile("index.html");
|
// reference: https://github.com/fastify/fastify-static?tab=readme-ov-file#managing-cache-control-headers
|
||||||
|
// to avoid ui bundle skew on new deployment
|
||||||
|
return reply.sendFile("index.html", { maxAge: 0, immutable: false });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import { externalKmsServiceFactory } from "@app/ee/services/external-kms/externa
|
|||||||
import { gatewayDALFactory } from "@app/ee/services/gateway/gateway-dal";
|
import { gatewayDALFactory } from "@app/ee/services/gateway/gateway-dal";
|
||||||
import { gatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
import { gatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
import { orgGatewayConfigDALFactory } from "@app/ee/services/gateway/org-gateway-config-dal";
|
import { orgGatewayConfigDALFactory } from "@app/ee/services/gateway/org-gateway-config-dal";
|
||||||
import { projectGatewayDALFactory } from "@app/ee/services/gateway/project-gateway-dal";
|
|
||||||
import { githubOrgSyncDALFactory } from "@app/ee/services/github-org-sync/github-org-sync-dal";
|
import { githubOrgSyncDALFactory } from "@app/ee/services/github-org-sync/github-org-sync-dal";
|
||||||
import { githubOrgSyncServiceFactory } from "@app/ee/services/github-org-sync/github-org-sync-service";
|
import { githubOrgSyncServiceFactory } from "@app/ee/services/github-org-sync/github-org-sync-service";
|
||||||
import { groupDALFactory } from "@app/ee/services/group/group-dal";
|
import { groupDALFactory } from "@app/ee/services/group/group-dal";
|
||||||
@@ -126,6 +125,7 @@ import { tokenDALFactory } from "@app/services/auth-token/auth-token-dal";
|
|||||||
import { tokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
import { tokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||||
import { certificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal";
|
import { certificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal";
|
||||||
import { certificateDALFactory } from "@app/services/certificate/certificate-dal";
|
import { certificateDALFactory } from "@app/services/certificate/certificate-dal";
|
||||||
|
import { certificateSecretDALFactory } from "@app/services/certificate/certificate-secret-dal";
|
||||||
import { certificateServiceFactory } from "@app/services/certificate/certificate-service";
|
import { certificateServiceFactory } from "@app/services/certificate/certificate-service";
|
||||||
import { certificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal";
|
import { certificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal";
|
||||||
import { certificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
|
import { certificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
|
||||||
@@ -167,6 +167,10 @@ import { identityJwtAuthDALFactory } from "@app/services/identity-jwt-auth/ident
|
|||||||
import { identityJwtAuthServiceFactory } from "@app/services/identity-jwt-auth/identity-jwt-auth-service";
|
import { identityJwtAuthServiceFactory } from "@app/services/identity-jwt-auth/identity-jwt-auth-service";
|
||||||
import { identityKubernetesAuthDALFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-dal";
|
import { identityKubernetesAuthDALFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-dal";
|
||||||
import { identityKubernetesAuthServiceFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-service";
|
import { identityKubernetesAuthServiceFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-service";
|
||||||
|
import { identityLdapAuthDALFactory } from "@app/services/identity-ldap-auth/identity-ldap-auth-dal";
|
||||||
|
import { identityLdapAuthServiceFactory } from "@app/services/identity-ldap-auth/identity-ldap-auth-service";
|
||||||
|
import { identityOciAuthDALFactory } from "@app/services/identity-oci-auth/identity-oci-auth-dal";
|
||||||
|
import { identityOciAuthServiceFactory } from "@app/services/identity-oci-auth/identity-oci-auth-service";
|
||||||
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";
|
||||||
@@ -202,6 +206,8 @@ import { pkiAlertServiceFactory } from "@app/services/pki-alert/pki-alert-servic
|
|||||||
import { pkiCollectionDALFactory } from "@app/services/pki-collection/pki-collection-dal";
|
import { pkiCollectionDALFactory } from "@app/services/pki-collection/pki-collection-dal";
|
||||||
import { pkiCollectionItemDALFactory } from "@app/services/pki-collection/pki-collection-item-dal";
|
import { pkiCollectionItemDALFactory } from "@app/services/pki-collection/pki-collection-item-dal";
|
||||||
import { pkiCollectionServiceFactory } from "@app/services/pki-collection/pki-collection-service";
|
import { pkiCollectionServiceFactory } from "@app/services/pki-collection/pki-collection-service";
|
||||||
|
import { pkiSubscriberDALFactory } from "@app/services/pki-subscriber/pki-subscriber-dal";
|
||||||
|
import { pkiSubscriberServiceFactory } from "@app/services/pki-subscriber/pki-subscriber-service";
|
||||||
import { projectDALFactory } from "@app/services/project/project-dal";
|
import { projectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { projectQueueFactory } from "@app/services/project/project-queue";
|
import { projectQueueFactory } from "@app/services/project/project-queue";
|
||||||
import { projectServiceFactory } from "@app/services/project/project-service";
|
import { projectServiceFactory } from "@app/services/project/project-service";
|
||||||
@@ -249,6 +255,7 @@ import { projectSlackConfigDALFactory } from "@app/services/slack/project-slack-
|
|||||||
import { slackIntegrationDALFactory } from "@app/services/slack/slack-integration-dal";
|
import { slackIntegrationDALFactory } from "@app/services/slack/slack-integration-dal";
|
||||||
import { slackServiceFactory } from "@app/services/slack/slack-service";
|
import { slackServiceFactory } from "@app/services/slack/slack-service";
|
||||||
import { TSmtpService } from "@app/services/smtp/smtp-service";
|
import { TSmtpService } from "@app/services/smtp/smtp-service";
|
||||||
|
import { invalidateCacheQueueFactory } from "@app/services/super-admin/invalidate-cache-queue";
|
||||||
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
|
||||||
import { getServerCfg, superAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
|
import { getServerCfg, superAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
|
||||||
import { telemetryDALFactory } from "@app/services/telemetry/telemetry-dal";
|
import { telemetryDALFactory } from "@app/services/telemetry/telemetry-dal";
|
||||||
@@ -357,9 +364,11 @@ export const registerRoutes = async (
|
|||||||
const identityUaClientSecretDAL = identityUaClientSecretDALFactory(db);
|
const identityUaClientSecretDAL = identityUaClientSecretDALFactory(db);
|
||||||
const identityAwsAuthDAL = identityAwsAuthDALFactory(db);
|
const identityAwsAuthDAL = identityAwsAuthDALFactory(db);
|
||||||
const identityGcpAuthDAL = identityGcpAuthDALFactory(db);
|
const identityGcpAuthDAL = identityGcpAuthDALFactory(db);
|
||||||
|
const identityOciAuthDAL = identityOciAuthDALFactory(db);
|
||||||
const identityOidcAuthDAL = identityOidcAuthDALFactory(db);
|
const identityOidcAuthDAL = identityOidcAuthDALFactory(db);
|
||||||
const identityJwtAuthDAL = identityJwtAuthDALFactory(db);
|
const identityJwtAuthDAL = identityJwtAuthDALFactory(db);
|
||||||
const identityAzureAuthDAL = identityAzureAuthDALFactory(db);
|
const identityAzureAuthDAL = identityAzureAuthDALFactory(db);
|
||||||
|
const identityLdapAuthDAL = identityLdapAuthDALFactory(db);
|
||||||
|
|
||||||
const auditLogDAL = auditLogDALFactory(auditLogDb ?? db);
|
const auditLogDAL = auditLogDALFactory(auditLogDb ?? db);
|
||||||
const auditLogStreamDAL = auditLogStreamDALFactory(db);
|
const auditLogStreamDAL = auditLogStreamDALFactory(db);
|
||||||
@@ -437,7 +446,6 @@ export const registerRoutes = async (
|
|||||||
|
|
||||||
const orgGatewayConfigDAL = orgGatewayConfigDALFactory(db);
|
const orgGatewayConfigDAL = orgGatewayConfigDALFactory(db);
|
||||||
const gatewayDAL = gatewayDALFactory(db);
|
const gatewayDAL = gatewayDALFactory(db);
|
||||||
const projectGatewayDAL = projectGatewayDALFactory(db);
|
|
||||||
const secretReminderRecipientsDAL = secretReminderRecipientsDALFactory(db);
|
const secretReminderRecipientsDAL = secretReminderRecipientsDALFactory(db);
|
||||||
const githubOrgSyncDAL = githubOrgSyncDALFactory(db);
|
const githubOrgSyncDAL = githubOrgSyncDALFactory(db);
|
||||||
|
|
||||||
@@ -649,6 +657,11 @@ export const registerRoutes = async (
|
|||||||
queueService
|
queueService
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const invalidateCacheQueue = invalidateCacheQueueFactory({
|
||||||
|
keyStore,
|
||||||
|
queueService
|
||||||
|
});
|
||||||
|
|
||||||
const userService = userServiceFactory({
|
const userService = userServiceFactory({
|
||||||
userDAL,
|
userDAL,
|
||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
@@ -760,7 +773,8 @@ export const registerRoutes = async (
|
|||||||
keyStore,
|
keyStore,
|
||||||
licenseService,
|
licenseService,
|
||||||
kmsService,
|
kmsService,
|
||||||
microsoftTeamsService
|
microsoftTeamsService,
|
||||||
|
invalidateCacheQueue
|
||||||
});
|
});
|
||||||
|
|
||||||
const orgAdminService = orgAdminServiceFactory({
|
const orgAdminService = orgAdminServiceFactory({
|
||||||
@@ -851,14 +865,17 @@ export const registerRoutes = async (
|
|||||||
|
|
||||||
const certificateDAL = certificateDALFactory(db);
|
const certificateDAL = certificateDALFactory(db);
|
||||||
const certificateBodyDAL = certificateBodyDALFactory(db);
|
const certificateBodyDAL = certificateBodyDALFactory(db);
|
||||||
|
const certificateSecretDAL = certificateSecretDALFactory(db);
|
||||||
|
|
||||||
const pkiAlertDAL = pkiAlertDALFactory(db);
|
const pkiAlertDAL = pkiAlertDALFactory(db);
|
||||||
const pkiCollectionDAL = pkiCollectionDALFactory(db);
|
const pkiCollectionDAL = pkiCollectionDALFactory(db);
|
||||||
const pkiCollectionItemDAL = pkiCollectionItemDALFactory(db);
|
const pkiCollectionItemDAL = pkiCollectionItemDALFactory(db);
|
||||||
|
const pkiSubscriberDAL = pkiSubscriberDALFactory(db);
|
||||||
|
|
||||||
const certificateService = certificateServiceFactory({
|
const certificateService = certificateServiceFactory({
|
||||||
certificateDAL,
|
certificateDAL,
|
||||||
certificateBodyDAL,
|
certificateBodyDAL,
|
||||||
|
certificateSecretDAL,
|
||||||
certificateAuthorityDAL,
|
certificateAuthorityDAL,
|
||||||
certificateAuthorityCertDAL,
|
certificateAuthorityCertDAL,
|
||||||
certificateAuthorityCrlDAL,
|
certificateAuthorityCrlDAL,
|
||||||
@@ -896,6 +913,8 @@ export const registerRoutes = async (
|
|||||||
|
|
||||||
const sshHostService = sshHostServiceFactory({
|
const sshHostService = sshHostServiceFactory({
|
||||||
userDAL,
|
userDAL,
|
||||||
|
groupDAL,
|
||||||
|
userGroupMembershipDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
projectSshConfigDAL,
|
projectSshConfigDAL,
|
||||||
sshCertificateAuthorityDAL,
|
sshCertificateAuthorityDAL,
|
||||||
@@ -918,7 +937,8 @@ export const registerRoutes = async (
|
|||||||
sshHostLoginUserMappingDAL,
|
sshHostLoginUserMappingDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService
|
licenseService,
|
||||||
|
groupDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const certificateAuthorityService = certificateAuthorityServiceFactory({
|
const certificateAuthorityService = certificateAuthorityServiceFactory({
|
||||||
@@ -930,6 +950,7 @@ export const registerRoutes = async (
|
|||||||
certificateAuthorityQueue,
|
certificateAuthorityQueue,
|
||||||
certificateDAL,
|
certificateDAL,
|
||||||
certificateBodyDAL,
|
certificateBodyDAL,
|
||||||
|
certificateSecretDAL,
|
||||||
pkiCollectionDAL,
|
pkiCollectionDAL,
|
||||||
pkiCollectionItemDAL,
|
pkiCollectionItemDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
@@ -984,6 +1005,20 @@ export const registerRoutes = async (
|
|||||||
projectDAL
|
projectDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const pkiSubscriberService = pkiSubscriberServiceFactory({
|
||||||
|
pkiSubscriberDAL,
|
||||||
|
certificateAuthorityDAL,
|
||||||
|
certificateAuthorityCertDAL,
|
||||||
|
certificateAuthoritySecretDAL,
|
||||||
|
certificateAuthorityCrlDAL,
|
||||||
|
certificateDAL,
|
||||||
|
certificateBodyDAL,
|
||||||
|
certificateSecretDAL,
|
||||||
|
projectDAL,
|
||||||
|
kmsService,
|
||||||
|
permissionService
|
||||||
|
});
|
||||||
|
|
||||||
const projectTemplateService = projectTemplateServiceFactory({
|
const projectTemplateService = projectTemplateServiceFactory({
|
||||||
licenseService,
|
licenseService,
|
||||||
permissionService,
|
permissionService,
|
||||||
@@ -1083,6 +1118,7 @@ export const registerRoutes = async (
|
|||||||
projectRoleDAL,
|
projectRoleDAL,
|
||||||
folderDAL,
|
folderDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
|
pkiSubscriberDAL,
|
||||||
certificateAuthorityDAL,
|
certificateAuthorityDAL,
|
||||||
certificateDAL,
|
certificateDAL,
|
||||||
pkiAlertDAL,
|
pkiAlertDAL,
|
||||||
@@ -1430,12 +1466,24 @@ export const registerRoutes = async (
|
|||||||
identityUaDAL,
|
identityUaDAL,
|
||||||
licenseService
|
licenseService
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const gatewayService = gatewayServiceFactory({
|
||||||
|
permissionService,
|
||||||
|
gatewayDAL,
|
||||||
|
kmsService,
|
||||||
|
licenseService,
|
||||||
|
orgGatewayConfigDAL,
|
||||||
|
keyStore
|
||||||
|
});
|
||||||
|
|
||||||
const identityKubernetesAuthService = identityKubernetesAuthServiceFactory({
|
const identityKubernetesAuthService = identityKubernetesAuthServiceFactory({
|
||||||
identityKubernetesAuthDAL,
|
identityKubernetesAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
|
gatewayService,
|
||||||
|
gatewayDAL,
|
||||||
kmsService
|
kmsService
|
||||||
});
|
});
|
||||||
const identityGcpAuthService = identityGcpAuthServiceFactory({
|
const identityGcpAuthService = identityGcpAuthServiceFactory({
|
||||||
@@ -1462,6 +1510,14 @@ export const registerRoutes = async (
|
|||||||
licenseService
|
licenseService
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const identityOciAuthService = identityOciAuthServiceFactory({
|
||||||
|
identityAccessTokenDAL,
|
||||||
|
identityOciAuthDAL,
|
||||||
|
identityOrgMembershipDAL,
|
||||||
|
licenseService,
|
||||||
|
permissionService
|
||||||
|
});
|
||||||
|
|
||||||
const identityOidcAuthService = identityOidcAuthServiceFactory({
|
const identityOidcAuthService = identityOidcAuthServiceFactory({
|
||||||
identityOidcAuthDAL,
|
identityOidcAuthDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
@@ -1480,14 +1536,14 @@ export const registerRoutes = async (
|
|||||||
kmsService
|
kmsService
|
||||||
});
|
});
|
||||||
|
|
||||||
const gatewayService = gatewayServiceFactory({
|
const identityLdapAuthService = identityLdapAuthServiceFactory({
|
||||||
|
identityLdapAuthDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
gatewayDAL,
|
|
||||||
kmsService,
|
kmsService,
|
||||||
|
identityAccessTokenDAL,
|
||||||
|
identityOrgMembershipDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
orgGatewayConfigDAL,
|
identityDAL
|
||||||
keyStore,
|
|
||||||
projectGatewayDAL
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const dynamicSecretProviders = buildDynamicSecretProviders({
|
const dynamicSecretProviders = buildDynamicSecretProviders({
|
||||||
@@ -1511,7 +1567,7 @@ export const registerRoutes = async (
|
|||||||
permissionService,
|
permissionService,
|
||||||
licenseService,
|
licenseService,
|
||||||
kmsService,
|
kmsService,
|
||||||
projectGatewayDAL,
|
gatewayDAL,
|
||||||
resourceMetadataDAL
|
resourceMetadataDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1741,8 +1797,10 @@ export const registerRoutes = async (
|
|||||||
identityGcpAuth: identityGcpAuthService,
|
identityGcpAuth: identityGcpAuthService,
|
||||||
identityAwsAuth: identityAwsAuthService,
|
identityAwsAuth: identityAwsAuthService,
|
||||||
identityAzureAuth: identityAzureAuthService,
|
identityAzureAuth: identityAzureAuthService,
|
||||||
|
identityOciAuth: identityOciAuthService,
|
||||||
identityOidcAuth: identityOidcAuthService,
|
identityOidcAuth: identityOidcAuthService,
|
||||||
identityJwtAuth: identityJwtAuthService,
|
identityJwtAuth: identityJwtAuthService,
|
||||||
|
identityLdapAuth: identityLdapAuthService,
|
||||||
accessApprovalPolicy: accessApprovalPolicyService,
|
accessApprovalPolicy: accessApprovalPolicyService,
|
||||||
accessApprovalRequest: accessApprovalRequestService,
|
accessApprovalRequest: accessApprovalRequestService,
|
||||||
secretApprovalPolicy: secretApprovalPolicyService,
|
secretApprovalPolicy: secretApprovalPolicyService,
|
||||||
@@ -1766,6 +1824,7 @@ export const registerRoutes = async (
|
|||||||
certificateEst: certificateEstService,
|
certificateEst: certificateEstService,
|
||||||
pkiAlert: pkiAlertService,
|
pkiAlert: pkiAlertService,
|
||||||
pkiCollection: pkiCollectionService,
|
pkiCollection: pkiCollectionService,
|
||||||
|
pkiSubscriber: pkiSubscriberService,
|
||||||
secretScanning: secretScanningService,
|
secretScanning: secretScanningService,
|
||||||
license: licenseService,
|
license: licenseService,
|
||||||
trustedIp: trustedIpService,
|
trustedIp: trustedIpService,
|
||||||
@@ -1809,6 +1868,10 @@ export const registerRoutes = async (
|
|||||||
if (licenseSyncJob) {
|
if (licenseSyncJob) {
|
||||||
cronJobs.push(licenseSyncJob);
|
cronJobs.push(licenseSyncJob);
|
||||||
}
|
}
|
||||||
|
const microsoftTeamsSyncJob = await microsoftTeamsService.initializeBackgroundSync();
|
||||||
|
if (microsoftTeamsSyncJob) {
|
||||||
|
cronJobs.push(microsoftTeamsSyncJob);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server.decorate<FastifyZodProvider["store"]>("store", {
|
server.decorate<FastifyZodProvider["store"]>("store", {
|
||||||
|
|||||||
@@ -261,7 +261,8 @@ export const SanitizedProjectSchema = ProjectsSchema.pick({
|
|||||||
pitVersionLimit: true,
|
pitVersionLimit: true,
|
||||||
kmsCertificateKeyId: true,
|
kmsCertificateKeyId: true,
|
||||||
auditLogsRetentionDays: true,
|
auditLogsRetentionDays: true,
|
||||||
hasDeleteProtection: true
|
hasDeleteProtection: true,
|
||||||
|
secretSharing: true
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SanitizedTagSchema = SecretTagsSchema.pick({
|
export const SanitizedTagSchema = SecretTagsSchema.pick({
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import { z } from "zod";
|
|||||||
import { IdentitiesSchema, OrganizationsSchema, SuperAdminSchema, UsersSchema } from "@app/db/schemas";
|
import { IdentitiesSchema, OrganizationsSchema, SuperAdminSchema, UsersSchema } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { invalidateCacheLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
|
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||||
import { verifySuperAdmin } from "@app/server/plugins/auth/superAdmin";
|
import { verifySuperAdmin } from "@app/server/plugins/auth/superAdmin";
|
||||||
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 { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types";
|
import { RootKeyEncryptionStrategy } from "@app/services/kms/kms-types";
|
||||||
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
|
||||||
import { LoginMethod } from "@app/services/super-admin/super-admin-types";
|
import { CacheType, LoginMethod } from "@app/services/super-admin/super-admin-types";
|
||||||
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||||
|
|
||||||
export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||||
@@ -548,4 +549,69 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/invalidate-cache",
|
||||||
|
config: {
|
||||||
|
rateLimit: invalidateCacheLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
type: z.nativeEnum(CacheType)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
message: z.string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: (req, res, done) => {
|
||||||
|
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||||
|
verifySuperAdmin(req, res, done);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
await server.services.superAdmin.invalidateCache(req.body.type);
|
||||||
|
|
||||||
|
await server.services.telemetry.sendPostHogEvents({
|
||||||
|
event: PostHogEventTypes.InvalidateCache,
|
||||||
|
distinctId: getTelemetryDistinctId(req),
|
||||||
|
properties: {
|
||||||
|
...req.auditLogInfo
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: "Cache invalidation job started"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/invalidating-cache-status",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
invalidating: z.boolean()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: (req, res, done) => {
|
||||||
|
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||||
|
verifySuperAdmin(req, res, done);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handler: async () => {
|
||||||
|
const invalidating = await server.services.superAdmin.checkIfInvalidatingCache();
|
||||||
|
|
||||||
|
return {
|
||||||
|
invalidating
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import {
|
|||||||
} from "@app/services/app-connection/humanitec";
|
} from "@app/services/app-connection/humanitec";
|
||||||
import { LdapConnectionListItemSchema, SanitizedLdapConnectionSchema } from "@app/services/app-connection/ldap";
|
import { LdapConnectionListItemSchema, SanitizedLdapConnectionSchema } from "@app/services/app-connection/ldap";
|
||||||
import { MsSqlConnectionListItemSchema, SanitizedMsSqlConnectionSchema } from "@app/services/app-connection/mssql";
|
import { MsSqlConnectionListItemSchema, SanitizedMsSqlConnectionSchema } from "@app/services/app-connection/mssql";
|
||||||
|
import { OCIConnectionListItemSchema, SanitizedOCIConnectionSchema } from "@app/services/app-connection/oci";
|
||||||
import {
|
import {
|
||||||
PostgresConnectionListItemSchema,
|
PostgresConnectionListItemSchema,
|
||||||
SanitizedPostgresConnectionSchema
|
SanitizedPostgresConnectionSchema
|
||||||
@@ -76,7 +77,8 @@ const SanitizedAppConnectionSchema = z.union([
|
|||||||
...SanitizedAzureClientSecretsConnectionSchema.options,
|
...SanitizedAzureClientSecretsConnectionSchema.options,
|
||||||
...SanitizedWindmillConnectionSchema.options,
|
...SanitizedWindmillConnectionSchema.options,
|
||||||
...SanitizedLdapConnectionSchema.options,
|
...SanitizedLdapConnectionSchema.options,
|
||||||
...SanitizedTeamCityConnectionSchema.options
|
...SanitizedTeamCityConnectionSchema.options,
|
||||||
|
...SanitizedOCIConnectionSchema.options
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
||||||
@@ -97,7 +99,8 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
|||||||
AzureClientSecretsConnectionListItemSchema,
|
AzureClientSecretsConnectionListItemSchema,
|
||||||
WindmillConnectionListItemSchema,
|
WindmillConnectionListItemSchema,
|
||||||
LdapConnectionListItemSchema,
|
LdapConnectionListItemSchema,
|
||||||
TeamCityConnectionListItemSchema
|
TeamCityConnectionListItemSchema,
|
||||||
|
OCIConnectionListItemSchema
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const registerAppConnectionRouter = async (server: FastifyZodProvider) => {
|
export const registerAppConnectionRouter = async (server: FastifyZodProvider) => {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user