mirror of
https://github.com/Infisical/infisical.git
synced 2025-08-07 17:58:25 +00:00
Compare commits
162 Commits
daniel/rus
...
docs/terra
Author | SHA1 | Date | |
---|---|---|---|
|
0ccc279805 | ||
|
7b160e0014 | ||
|
c31ede99b1 | ||
|
71f609fa4f | ||
|
8b963479a9 | ||
|
932e87f3e4 | ||
|
4f51ade2cd | ||
|
f3216800eb | ||
|
7d1bc86702 | ||
|
975b621bc8 | ||
|
ba9da3e6ec | ||
|
d2274a622a | ||
|
41ba7edba2 | ||
|
7acefbca29 | ||
|
e246f6bbfe | ||
|
f265fa6d37 | ||
|
8eebd7228f | ||
|
2a5593ea30 | ||
|
17af33372c | ||
|
27da14df9d | ||
|
cd4b9cd03a | ||
|
0779091d1f | ||
|
c421057cf1 | ||
|
8df4616265 | ||
|
484f34a257 | ||
|
32851565a7 | ||
|
68401a799e | ||
|
0adf2c830d | ||
|
3400a8f911 | ||
|
e6588b5d0e | ||
|
c68138ac21 | ||
|
608979efa7 | ||
|
d4f0301104 | ||
|
253c46f21d | ||
|
d8e39aed16 | ||
|
72ee468208 | ||
|
18238b46a7 | ||
|
d0ffae2c10 | ||
|
7ce11cde95 | ||
|
af32948a05 | ||
|
25753fc995 | ||
|
cd71848800 | ||
|
4afc7a1981 | ||
|
11ca76ccca | ||
|
418aca8af0 | ||
|
99e8bdef58 | ||
|
7365f60835 | ||
|
929822514e | ||
|
616ccb97f2 | ||
|
7917a767e6 | ||
|
ccff675e0d | ||
|
ad905b2ff7 | ||
|
4e960445a4 | ||
|
7af5a4ad8d | ||
|
2ada753527 | ||
|
c031736701 | ||
|
91a1c34637 | ||
|
eadb1a63fa | ||
|
f70a1e3db6 | ||
|
fc6ab94a06 | ||
|
4feb3314e7 | ||
|
d9a57d1391 | ||
|
2c99d41592 | ||
|
2535d1bc4b | ||
|
83e59ae160 | ||
|
a8a1bc5f4a | ||
|
d2a4f265de | ||
|
3483f185a8 | ||
|
9bc24487b3 | ||
|
4af872e504 | ||
|
716b88fa49 | ||
|
b05ea8a69a | ||
|
0d97bb4c8c | ||
|
cb700c5124 | ||
|
8e829bdf85 | ||
|
716f061c01 | ||
|
5af939992c | ||
|
aec4ee905e | ||
|
dd008724fb | ||
|
dd0c07fb95 | ||
|
d935b28925 | ||
|
60620840f2 | ||
|
e798eb2a4e | ||
|
e96e7b835d | ||
|
75622ed03e | ||
|
a7041fcade | ||
|
0b38fc7843 | ||
|
e678c19874 | ||
|
878e12ea5c | ||
|
485a90bde1 | ||
|
98b6bdad76 | ||
|
f490ca22ac | ||
|
2d8de9e782 | ||
|
14d4cfdbe4 | ||
|
e8bd73c0d0 | ||
|
3406457c08 | ||
|
c16764b62b | ||
|
ab56a69d59 | ||
|
8520ca98c7 | ||
|
95b997c100 | ||
|
b433582ca6 | ||
|
242cfe82c5 | ||
|
60657f0bc6 | ||
|
af4f7ec4f3 | ||
|
454e75cfd0 | ||
|
05408bc151 | ||
|
95f8ae1cf8 | ||
|
feb773152e | ||
|
7f35ff119e | ||
|
cb4cb922b9 | ||
|
dfecaae560 | ||
|
53bec6bc3e | ||
|
af48e7ce99 | ||
|
9f35b573d1 | ||
|
bcb1f35606 | ||
|
67ab16aff3 | ||
|
354aed5e8a | ||
|
e2e9dbc8aa | ||
|
f38b8eac2b | ||
|
7c87feb546 | ||
|
e0cbfe8865 | ||
|
abda494374 | ||
|
272207c580 | ||
|
4cf66a8bfd | ||
|
30ef7f395a | ||
|
ec8ea76e2c | ||
|
cc9f4fb5b3 | ||
|
33256c3462 | ||
|
864be1deb7 | ||
|
f10ab58d74 | ||
|
9ec4419d83 | ||
|
7ff7e5882a | ||
|
e76e0f7bcc | ||
|
cb4999c1b4 | ||
|
d4bdf04061 | ||
|
4dcb3938e0 | ||
|
f992535812 | ||
|
464e32b0e9 | ||
|
4547b61d8f | ||
|
047fd9371f | ||
|
bfd8b64871 | ||
|
185cc4efba | ||
|
7150b9314d | ||
|
328f929a29 | ||
|
5019918516 | ||
|
ce877cd352 | ||
|
d44b3293b6 | ||
|
4d8000e331 | ||
|
90c341cf53 | ||
|
8df53dde3b | ||
|
394ecd24a0 | ||
|
6d3acb5514 | ||
|
1e08b3cdc2 | ||
|
844f2bb72c | ||
|
bd4968b60d | ||
|
6449699f03 | ||
|
0e680e366b | ||
|
0af00ce82d | ||
|
3153450dc5 | ||
|
50ba2e543c | ||
|
e2559f10bc | ||
|
0efc314f33 |
13
.env.example
13
.env.example
@@ -123,8 +123,17 @@ INF_APP_CONNECTION_GITHUB_RADAR_APP_WEBHOOK_SECRET=
|
|||||||
INF_APP_CONNECTION_GCP_SERVICE_ACCOUNT_CREDENTIAL=
|
INF_APP_CONNECTION_GCP_SERVICE_ACCOUNT_CREDENTIAL=
|
||||||
|
|
||||||
# azure app connection
|
# azure app connection
|
||||||
INF_APP_CONNECTION_AZURE_CLIENT_ID=
|
INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_ID=
|
||||||
INF_APP_CONNECTION_AZURE_CLIENT_SECRET=
|
INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_SECRET=
|
||||||
|
|
||||||
|
INF_APP_CONNECTION_AZURE_KEY_VAULT_CLIENT_ID=
|
||||||
|
INF_APP_CONNECTION_AZURE_KEY_VAULT_CLIENT_SECRET=
|
||||||
|
|
||||||
|
INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_ID=
|
||||||
|
INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_SECRET=
|
||||||
|
|
||||||
|
INF_APP_CONNECTION_AZURE_DEVOPS_CLIENT_ID=
|
||||||
|
INF_APP_CONNECTION_AZURE_DEVOPS_CLIENT_SECRET=
|
||||||
|
|
||||||
# datadog
|
# datadog
|
||||||
SHOULD_USE_DATADOG_TRACER=
|
SHOULD_USE_DATADOG_TRACER=
|
||||||
|
@@ -145,7 +145,11 @@ RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
|
|||||||
&& cd openssl-3.1.2 \
|
&& cd openssl-3.1.2 \
|
||||||
&& ./Configure enable-fips \
|
&& ./Configure enable-fips \
|
||||||
&& make \
|
&& make \
|
||||||
&& make install_fips
|
&& make install_fips \
|
||||||
|
&& cd / \
|
||||||
|
&& rm -rf /openssl-build \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
# Install Infisical CLI
|
# Install Infisical CLI
|
||||||
RUN curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash \
|
RUN curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash \
|
||||||
@@ -186,12 +190,11 @@ 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"
|
ENV NODE_OPTIONS="--max-old-space-size=8192 --force-fips"
|
||||||
|
|
||||||
# FIPS mode of operation:
|
# FIPS mode of operation:
|
||||||
ENV OPENSSL_CONF=/backend/nodejs.fips.cnf
|
ENV OPENSSL_CONF=/backend/nodejs.fips.cnf
|
||||||
ENV OPENSSL_MODULES=/usr/local/lib/ossl-modules
|
ENV OPENSSL_MODULES=/usr/local/lib/ossl-modules
|
||||||
ENV NODE_OPTIONS=--force-fips
|
|
||||||
ENV FIPS_ENABLED=true
|
ENV FIPS_ENABLED=true
|
||||||
|
|
||||||
|
|
||||||
|
@@ -59,7 +59,11 @@ RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
|
|||||||
&& cd openssl-3.1.2 \
|
&& cd openssl-3.1.2 \
|
||||||
&& ./Configure enable-fips \
|
&& ./Configure enable-fips \
|
||||||
&& make \
|
&& make \
|
||||||
&& make install_fips
|
&& make install_fips \
|
||||||
|
&& cd / \
|
||||||
|
&& rm -rf /openssl-build \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
# ? App setup
|
# ? App setup
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@ export const mockQueue = (): TQueueServiceFactory => {
|
|||||||
events[name] = event;
|
events[name] = event;
|
||||||
},
|
},
|
||||||
getRepeatableJobs: async () => [],
|
getRepeatableJobs: async () => [],
|
||||||
|
getDelayedJobs: async () => [],
|
||||||
clearQueue: async () => {},
|
clearQueue: async () => {},
|
||||||
stopJobById: async () => {},
|
stopJobById: async () => {},
|
||||||
stopJobByIdPg: async () => {},
|
stopJobByIdPg: async () => {},
|
||||||
|
31
backend/package-lock.json
generated
31
backend/package-lock.json
generated
@@ -7,6 +7,7 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "backend",
|
"name": "backend",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"hasInstallScript": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-elasticache": "^3.637.0",
|
"@aws-sdk/client-elasticache": "^3.637.0",
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
"ajv": "^8.12.0",
|
"ajv": "^8.12.0",
|
||||||
"argon2": "^0.31.2",
|
"argon2": "^0.31.2",
|
||||||
"aws-sdk": "^2.1553.0",
|
"aws-sdk": "^2.1553.0",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.11.0",
|
||||||
"axios-retry": "^4.0.0",
|
"axios-retry": "^4.0.0",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"botbuilder": "^4.23.2",
|
"botbuilder": "^4.23.2",
|
||||||
@@ -13699,14 +13700,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/request/node_modules/form-data": {
|
"node_modules/@types/request/node_modules/form-data": {
|
||||||
"version": "2.5.2",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz",
|
||||||
"integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==",
|
"integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "^1.0.6",
|
"combined-stream": "^1.0.8",
|
||||||
"mime-types": "^2.1.12",
|
"es-set-tostringtag": "^2.1.0",
|
||||||
|
"hasown": "^2.0.2",
|
||||||
|
"mime-types": "^2.1.35",
|
||||||
"safe-buffer": "^5.2.1"
|
"safe-buffer": "^5.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -15230,13 +15233,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.7.9",
|
"version": "1.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz",
|
||||||
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
|
"integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.6",
|
"follow-redirects": "^1.15.6",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.4",
|
||||||
"proxy-from-env": "^1.1.0"
|
"proxy-from-env": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -18761,13 +18764,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/form-data": {
|
"node_modules/form-data": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||||
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
|
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"asynckit": "^0.4.0",
|
"asynckit": "^0.4.0",
|
||||||
"combined-stream": "^1.0.8",
|
"combined-stream": "^1.0.8",
|
||||||
"es-set-tostringtag": "^2.1.0",
|
"es-set-tostringtag": "^2.1.0",
|
||||||
|
"hasown": "^2.0.2",
|
||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.12"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@@ -181,7 +181,7 @@
|
|||||||
"ajv": "^8.12.0",
|
"ajv": "^8.12.0",
|
||||||
"argon2": "^0.31.2",
|
"argon2": "^0.31.2",
|
||||||
"aws-sdk": "^2.1553.0",
|
"aws-sdk": "^2.1553.0",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.11.0",
|
||||||
"axios-retry": "^4.0.0",
|
"axios-retry": "^4.0.0",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"botbuilder": "^4.23.2",
|
"botbuilder": "^4.23.2",
|
||||||
|
11
backend/src/@types/fastify.d.ts
vendored
11
backend/src/@types/fastify.d.ts
vendored
@@ -93,6 +93,7 @@ import { TProjectEnvServiceFactory } from "@app/services/project-env/project-env
|
|||||||
import { TProjectKeyServiceFactory } from "@app/services/project-key/project-key-service";
|
import { TProjectKeyServiceFactory } from "@app/services/project-key/project-key-service";
|
||||||
import { TProjectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service";
|
import { TProjectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service";
|
||||||
import { TProjectRoleServiceFactory } from "@app/services/project-role/project-role-service";
|
import { TProjectRoleServiceFactory } from "@app/services/project-role/project-role-service";
|
||||||
|
import { TReminderServiceFactory } from "@app/services/reminder/reminder-types";
|
||||||
import { TSecretServiceFactory } from "@app/services/secret/secret-service";
|
import { TSecretServiceFactory } from "@app/services/secret/secret-service";
|
||||||
import { TSecretBlindIndexServiceFactory } from "@app/services/secret-blind-index/secret-blind-index-service";
|
import { TSecretBlindIndexServiceFactory } from "@app/services/secret-blind-index/secret-blind-index-service";
|
||||||
import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
|
import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
|
||||||
@@ -125,6 +126,15 @@ declare module "@fastify/request-context" {
|
|||||||
namespace: string;
|
namespace: string;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
aws?: {
|
||||||
|
accountId: string;
|
||||||
|
arn: string;
|
||||||
|
userId: string;
|
||||||
|
partition: string;
|
||||||
|
service: string;
|
||||||
|
resourceType: string;
|
||||||
|
resourceName: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
identityPermissionMetadata?: Record<string, unknown>; // filled by permission service
|
identityPermissionMetadata?: Record<string, unknown>; // filled by permission service
|
||||||
assumedPrivilegeDetails?: { requesterId: string; actorId: string; actorType: ActorType; projectId: string };
|
assumedPrivilegeDetails?: { requesterId: string; actorId: string; actorType: ActorType; projectId: string };
|
||||||
@@ -285,6 +295,7 @@ declare module "fastify" {
|
|||||||
secretScanningV2: TSecretScanningV2ServiceFactory;
|
secretScanningV2: TSecretScanningV2ServiceFactory;
|
||||||
internalCertificateAuthority: TInternalCertificateAuthorityServiceFactory;
|
internalCertificateAuthority: TInternalCertificateAuthorityServiceFactory;
|
||||||
pkiTemplate: TPkiTemplatesServiceFactory;
|
pkiTemplate: TPkiTemplatesServiceFactory;
|
||||||
|
reminder: TReminderServiceFactory;
|
||||||
};
|
};
|
||||||
// this is exclusive use for middlewares in which we need to inject data
|
// this is exclusive use for middlewares in which we need to inject data
|
||||||
// everywhere else access using service layer
|
// everywhere else access using service layer
|
||||||
|
33
backend/src/@types/knex.d.ts
vendored
33
backend/src/@types/knex.d.ts
vendored
@@ -489,6 +489,11 @@ import {
|
|||||||
TWorkflowIntegrationsInsert,
|
TWorkflowIntegrationsInsert,
|
||||||
TWorkflowIntegrationsUpdate
|
TWorkflowIntegrationsUpdate
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
|
import {
|
||||||
|
TAccessApprovalPoliciesEnvironments,
|
||||||
|
TAccessApprovalPoliciesEnvironmentsInsert,
|
||||||
|
TAccessApprovalPoliciesEnvironmentsUpdate
|
||||||
|
} from "@app/db/schemas/access-approval-policies-environments";
|
||||||
import {
|
import {
|
||||||
TIdentityLdapAuths,
|
TIdentityLdapAuths,
|
||||||
TIdentityLdapAuthsInsert,
|
TIdentityLdapAuthsInsert,
|
||||||
@@ -504,6 +509,17 @@ import {
|
|||||||
TProjectMicrosoftTeamsConfigsInsert,
|
TProjectMicrosoftTeamsConfigsInsert,
|
||||||
TProjectMicrosoftTeamsConfigsUpdate
|
TProjectMicrosoftTeamsConfigsUpdate
|
||||||
} from "@app/db/schemas/project-microsoft-teams-configs";
|
} from "@app/db/schemas/project-microsoft-teams-configs";
|
||||||
|
import { TReminders, TRemindersInsert, TRemindersUpdate } from "@app/db/schemas/reminders";
|
||||||
|
import {
|
||||||
|
TRemindersRecipients,
|
||||||
|
TRemindersRecipientsInsert,
|
||||||
|
TRemindersRecipientsUpdate
|
||||||
|
} from "@app/db/schemas/reminders-recipients";
|
||||||
|
import {
|
||||||
|
TSecretApprovalPoliciesEnvironments,
|
||||||
|
TSecretApprovalPoliciesEnvironmentsInsert,
|
||||||
|
TSecretApprovalPoliciesEnvironmentsUpdate
|
||||||
|
} from "@app/db/schemas/secret-approval-policies-environments";
|
||||||
import {
|
import {
|
||||||
TSecretReminderRecipients,
|
TSecretReminderRecipients,
|
||||||
TSecretReminderRecipientsInsert,
|
TSecretReminderRecipientsInsert,
|
||||||
@@ -881,6 +897,12 @@ declare module "knex/types/tables" {
|
|||||||
TAccessApprovalPoliciesBypassersUpdate
|
TAccessApprovalPoliciesBypassersUpdate
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
[TableName.AccessApprovalPolicyEnvironment]: KnexOriginal.CompositeTableType<
|
||||||
|
TAccessApprovalPoliciesEnvironments,
|
||||||
|
TAccessApprovalPoliciesEnvironmentsInsert,
|
||||||
|
TAccessApprovalPoliciesEnvironmentsUpdate
|
||||||
|
>;
|
||||||
|
|
||||||
[TableName.AccessApprovalRequest]: KnexOriginal.CompositeTableType<
|
[TableName.AccessApprovalRequest]: KnexOriginal.CompositeTableType<
|
||||||
TAccessApprovalRequests,
|
TAccessApprovalRequests,
|
||||||
TAccessApprovalRequestsInsert,
|
TAccessApprovalRequestsInsert,
|
||||||
@@ -929,6 +951,11 @@ declare module "knex/types/tables" {
|
|||||||
TSecretApprovalRequestSecretTagsInsert,
|
TSecretApprovalRequestSecretTagsInsert,
|
||||||
TSecretApprovalRequestSecretTagsUpdate
|
TSecretApprovalRequestSecretTagsUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.SecretApprovalPolicyEnvironment]: KnexOriginal.CompositeTableType<
|
||||||
|
TSecretApprovalPoliciesEnvironments,
|
||||||
|
TSecretApprovalPoliciesEnvironmentsInsert,
|
||||||
|
TSecretApprovalPoliciesEnvironmentsUpdate
|
||||||
|
>;
|
||||||
[TableName.SecretRotation]: KnexOriginal.CompositeTableType<
|
[TableName.SecretRotation]: KnexOriginal.CompositeTableType<
|
||||||
TSecretRotations,
|
TSecretRotations,
|
||||||
TSecretRotationsInsert,
|
TSecretRotationsInsert,
|
||||||
@@ -1211,5 +1238,11 @@ declare module "knex/types/tables" {
|
|||||||
TSecretScanningConfigsInsert,
|
TSecretScanningConfigsInsert,
|
||||||
TSecretScanningConfigsUpdate
|
TSecretScanningConfigsUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.Reminder]: KnexOriginal.CompositeTableType<TReminders, TRemindersInsert, TRemindersUpdate>;
|
||||||
|
[TableName.ReminderRecipient]: KnexOriginal.CompositeTableType<
|
||||||
|
TRemindersRecipients,
|
||||||
|
TRemindersRecipientsInsert,
|
||||||
|
TRemindersRecipientsUpdate
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,43 @@
|
|||||||
|
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.Reminder))) {
|
||||||
|
await knex.schema.createTable(TableName.Reminder, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.uuid("secretId").nullable();
|
||||||
|
t.foreign("secretId").references("id").inTable(TableName.SecretV2).onDelete("CASCADE");
|
||||||
|
t.string("message", 1024).nullable();
|
||||||
|
t.integer("repeatDays").checkPositive().nullable();
|
||||||
|
t.timestamp("nextReminderDate").notNullable();
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
t.unique("secretId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasTable(TableName.ReminderRecipient))) {
|
||||||
|
await knex.schema.createTable(TableName.ReminderRecipient, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.uuid("reminderId").notNullable();
|
||||||
|
t.foreign("reminderId").references("id").inTable(TableName.Reminder).onDelete("CASCADE");
|
||||||
|
t.uuid("userId").notNullable();
|
||||||
|
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
t.index("reminderId");
|
||||||
|
t.index("userId");
|
||||||
|
t.unique(["reminderId", "userId"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.Reminder);
|
||||||
|
await createOnUpdateTrigger(knex, TableName.ReminderRecipient);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.Reminder);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.ReminderRecipient);
|
||||||
|
await knex.schema.dropTableIfExists(TableName.ReminderRecipient);
|
||||||
|
await knex.schema.dropTableIfExists(TableName.Reminder);
|
||||||
|
}
|
432
backend/src/db/migrations/20250718133527_project-unify-revert.ts
Normal file
432
backend/src/db/migrations/20250718133527_project-unify-revert.ts
Normal file
@@ -0,0 +1,432 @@
|
|||||||
|
import slugify from "@sindresorhus/slugify";
|
||||||
|
import { Knex } from "knex";
|
||||||
|
import { v4 as uuidV4 } from "uuid";
|
||||||
|
|
||||||
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
|
||||||
|
import { ProjectType, TableName } from "../schemas";
|
||||||
|
|
||||||
|
/* eslint-disable no-await-in-loop,@typescript-eslint/ban-ts-comment */
|
||||||
|
|
||||||
|
// Single query to get all projects that need any kind of kickout
|
||||||
|
const getProjectsNeedingKickouts = async (
|
||||||
|
knex: Knex
|
||||||
|
): Promise<
|
||||||
|
Array<{
|
||||||
|
id: string;
|
||||||
|
defaultProduct: string;
|
||||||
|
needsSecretManager: boolean;
|
||||||
|
needsCertManager: boolean;
|
||||||
|
needsSecretScanning: boolean;
|
||||||
|
needsKms: boolean;
|
||||||
|
needsSsh: boolean;
|
||||||
|
}>
|
||||||
|
> => {
|
||||||
|
const result = await knex.raw(
|
||||||
|
`
|
||||||
|
SELECT DISTINCT
|
||||||
|
p.id,
|
||||||
|
p."defaultProduct",
|
||||||
|
|
||||||
|
-- Use CASE with direct joins instead of EXISTS subqueries
|
||||||
|
CASE WHEN p."defaultProduct" != 'secret-manager' AND s.secret_exists IS NOT NULL THEN true ELSE false END AS "needsSecretManager",
|
||||||
|
CASE WHEN p."defaultProduct" != 'cert-manager' AND ca.ca_exists IS NOT NULL THEN true ELSE false END AS "needsCertManager",
|
||||||
|
CASE WHEN p."defaultProduct" != 'secret-scanning' AND ssds.ssds_exists IS NOT NULL THEN true ELSE false END AS "needsSecretScanning",
|
||||||
|
CASE WHEN p."defaultProduct" != 'kms' AND kk.kms_exists IS NOT NULL THEN true ELSE false END AS "needsKms",
|
||||||
|
CASE WHEN p."defaultProduct" != 'ssh' AND sc.ssh_exists IS NOT NULL THEN true ELSE false END AS "needsSsh"
|
||||||
|
|
||||||
|
FROM projects p
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT DISTINCT e."projectId", 1 as secret_exists
|
||||||
|
FROM secrets_v2 s
|
||||||
|
JOIN secret_folders sf ON sf.id = s."folderId"
|
||||||
|
JOIN project_environments e ON e.id = sf."envId"
|
||||||
|
) s ON s."projectId" = p.id AND p."defaultProduct" != 'secret-manager'
|
||||||
|
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT DISTINCT "projectId", 1 as ca_exists
|
||||||
|
FROM certificate_authorities
|
||||||
|
) ca ON ca."projectId" = p.id AND p."defaultProduct" != 'cert-manager'
|
||||||
|
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT DISTINCT "projectId", 1 as ssds_exists
|
||||||
|
FROM secret_scanning_data_sources
|
||||||
|
) ssds ON ssds."projectId" = p.id AND p."defaultProduct" != 'secret-scanning'
|
||||||
|
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT DISTINCT "projectId", 1 as kms_exists
|
||||||
|
FROM kms_keys
|
||||||
|
WHERE "isReserved" = false
|
||||||
|
) kk ON kk."projectId" = p.id AND p."defaultProduct" != 'kms'
|
||||||
|
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT DISTINCT sca."projectId", 1 as ssh_exists
|
||||||
|
FROM ssh_certificates sc
|
||||||
|
JOIN ssh_certificate_authorities sca ON sca.id = sc."sshCaId"
|
||||||
|
) sc ON sc."projectId" = p.id AND p."defaultProduct" != 'ssh'
|
||||||
|
|
||||||
|
WHERE p."defaultProduct" IS NOT NULL
|
||||||
|
AND (
|
||||||
|
(p."defaultProduct" != 'secret-manager' AND s.secret_exists IS NOT NULL) OR
|
||||||
|
(p."defaultProduct" != 'cert-manager' AND ca.ca_exists IS NOT NULL) OR
|
||||||
|
(p."defaultProduct" != 'secret-scanning' AND ssds.ssds_exists IS NOT NULL) OR
|
||||||
|
(p."defaultProduct" != 'kms' AND kk.kms_exists IS NOT NULL) OR
|
||||||
|
(p."defaultProduct" != 'ssh' AND sc.ssh_exists IS NOT NULL)
|
||||||
|
)
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
return result.rows;
|
||||||
|
};
|
||||||
|
|
||||||
|
const newProject = async (knex: Knex, projectId: string, projectType: ProjectType) => {
|
||||||
|
const newProjectId = uuidV4();
|
||||||
|
const project = await knex(TableName.Project).where("id", projectId).first();
|
||||||
|
await knex(TableName.Project).insert({
|
||||||
|
...project,
|
||||||
|
type: projectType,
|
||||||
|
defaultProduct: null,
|
||||||
|
// @ts-ignore id is required
|
||||||
|
id: newProjectId,
|
||||||
|
slug: slugify(`${project?.name}-${alphaNumericNanoId(8)}`)
|
||||||
|
});
|
||||||
|
|
||||||
|
const customRoleMapping: Record<string, string> = {};
|
||||||
|
const projectCustomRoles = await knex(TableName.ProjectRoles).where("projectId", projectId);
|
||||||
|
if (projectCustomRoles.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.ProjectRoles,
|
||||||
|
projectCustomRoles.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
customRoleMapping[el.id] = id;
|
||||||
|
return {
|
||||||
|
...el,
|
||||||
|
id,
|
||||||
|
projectId: newProjectId,
|
||||||
|
permissions: el.permissions ? JSON.stringify(el.permissions) : el.permissions
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const groupMembershipMapping: Record<string, string> = {};
|
||||||
|
const groupMemberships = await knex(TableName.GroupProjectMembership).where("projectId", projectId);
|
||||||
|
if (groupMemberships.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.GroupProjectMembership,
|
||||||
|
groupMemberships.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
groupMembershipMapping[el.id] = id;
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupMembershipRoles = await knex(TableName.GroupProjectMembershipRole).whereIn(
|
||||||
|
"projectMembershipId",
|
||||||
|
groupMemberships.map((el) => el.id)
|
||||||
|
);
|
||||||
|
if (groupMembershipRoles.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.GroupProjectMembershipRole,
|
||||||
|
groupMembershipRoles.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
const projectMembershipId = groupMembershipMapping[el.projectMembershipId];
|
||||||
|
const customRoleId = el.customRoleId ? customRoleMapping[el.customRoleId] : el.customRoleId;
|
||||||
|
return { ...el, id, projectMembershipId, customRoleId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const identityProjectMembershipMapping: Record<string, string> = {};
|
||||||
|
const identities = await knex(TableName.IdentityProjectMembership).where("projectId", projectId);
|
||||||
|
if (identities.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.IdentityProjectMembership,
|
||||||
|
identities.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
identityProjectMembershipMapping[el.id] = id;
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const identitiesRoles = await knex(TableName.IdentityProjectMembershipRole).whereIn(
|
||||||
|
"projectMembershipId",
|
||||||
|
identities.map((el) => el.id)
|
||||||
|
);
|
||||||
|
if (identitiesRoles.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.IdentityProjectMembershipRole,
|
||||||
|
identitiesRoles.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
const projectMembershipId = identityProjectMembershipMapping[el.projectMembershipId];
|
||||||
|
const customRoleId = el.customRoleId ? customRoleMapping[el.customRoleId] : el.customRoleId;
|
||||||
|
return { ...el, id, projectMembershipId, customRoleId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectMembershipMapping: Record<string, string> = {};
|
||||||
|
const projectUserMembers = await knex(TableName.ProjectMembership).where("projectId", projectId);
|
||||||
|
if (projectUserMembers.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.ProjectMembership,
|
||||||
|
projectUserMembers.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
projectMembershipMapping[el.id] = id;
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const membershipRoles = await knex(TableName.ProjectUserMembershipRole).whereIn(
|
||||||
|
"projectMembershipId",
|
||||||
|
projectUserMembers.map((el) => el.id)
|
||||||
|
);
|
||||||
|
if (membershipRoles.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.ProjectUserMembershipRole,
|
||||||
|
membershipRoles.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
const projectMembershipId = projectMembershipMapping[el.projectMembershipId];
|
||||||
|
const customRoleId = el.customRoleId ? customRoleMapping[el.customRoleId] : el.customRoleId;
|
||||||
|
return { ...el, id, projectMembershipId, customRoleId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const kmsKeys = await knex(TableName.KmsKey).where("projectId", projectId).andWhere("isReserved", true);
|
||||||
|
if (kmsKeys.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.KmsKey,
|
||||||
|
kmsKeys.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
const slug = slugify(alphaNumericNanoId(8).toLowerCase());
|
||||||
|
return { ...el, id, slug, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectBot = await knex(TableName.ProjectBot).where("projectId", projectId).first();
|
||||||
|
if (projectBot) {
|
||||||
|
const newProjectBot = { ...projectBot, id: uuidV4(), projectId: newProjectId };
|
||||||
|
await knex(TableName.ProjectBot).insert(newProjectBot);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectKeys = await knex(TableName.ProjectKeys).where("projectId", projectId);
|
||||||
|
if (projectKeys.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.ProjectKeys,
|
||||||
|
projectKeys.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectGateways = await knex(TableName.ProjectGateway).where("projectId", projectId);
|
||||||
|
if (projectGateways.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.ProjectGateway,
|
||||||
|
projectGateways.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectSlackConfigs = await knex(TableName.ProjectSlackConfigs).where("projectId", projectId);
|
||||||
|
if (projectSlackConfigs.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.ProjectSlackConfigs,
|
||||||
|
projectSlackConfigs.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectMicrosoftTeamsConfigs = await knex(TableName.ProjectMicrosoftTeamsConfigs).where("projectId", projectId);
|
||||||
|
if (projectMicrosoftTeamsConfigs.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.ProjectMicrosoftTeamsConfigs,
|
||||||
|
projectMicrosoftTeamsConfigs.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const trustedIps = await knex(TableName.TrustedIps).where("projectId", projectId);
|
||||||
|
if (trustedIps.length) {
|
||||||
|
await knex.batchInsert(
|
||||||
|
TableName.TrustedIps,
|
||||||
|
trustedIps.map((el) => {
|
||||||
|
const id = uuidV4();
|
||||||
|
return { ...el, id, projectId: newProjectId };
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newProjectId;
|
||||||
|
};
|
||||||
|
|
||||||
|
const kickOutSecretManagerProject = async (knex: Knex, oldProjectId: string) => {
|
||||||
|
const newProjectId = await newProject(knex, oldProjectId, ProjectType.SecretManager);
|
||||||
|
await knex(TableName.IntegrationAuth).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.Environment).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SecretBlindIndex).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SecretSync).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SecretTag).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SecretReminderRecipients).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.ServiceToken).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const kickOutCertManagerProject = async (knex: Knex, oldProjectId: string) => {
|
||||||
|
const newProjectId = await newProject(knex, oldProjectId, ProjectType.CertificateManager);
|
||||||
|
await knex(TableName.CertificateAuthority).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.Certificate).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.PkiSubscriber).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.PkiCollection).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.PkiAlert).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const kickOutSecretScanningProject = async (knex: Knex, oldProjectId: string) => {
|
||||||
|
const newProjectId = await newProject(knex, oldProjectId, ProjectType.SecretScanning);
|
||||||
|
await knex(TableName.SecretScanningConfig).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SecretScanningDataSource).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SecretScanningFinding).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const kickOutKmsProject = async (knex: Knex, oldProjectId: string) => {
|
||||||
|
const newProjectId = await newProject(knex, oldProjectId, ProjectType.KMS);
|
||||||
|
await knex(TableName.KmsKey)
|
||||||
|
.where("projectId", oldProjectId)
|
||||||
|
.andWhere("isReserved", false)
|
||||||
|
.update("projectId", newProjectId);
|
||||||
|
await knex(TableName.KmipClient).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const kickOutSshProject = async (knex: Knex, oldProjectId: string) => {
|
||||||
|
const newProjectId = await newProject(knex, oldProjectId, ProjectType.SSH);
|
||||||
|
await knex(TableName.SshHost).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.ProjectSshConfig).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SshCertificateAuthority).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
await knex(TableName.SshHostGroup).where("projectId", oldProjectId).update("projectId", newProjectId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const BATCH_SIZE = 1000;
|
||||||
|
const MIGRATION_TIMEOUT = 30 * 60 * 1000; // 30 minutes
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const result = await knex.raw("SHOW statement_timeout");
|
||||||
|
const originalTimeout = result.rows[0].statement_timeout;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await knex.raw(`SET statement_timeout = ${MIGRATION_TIMEOUT}`);
|
||||||
|
|
||||||
|
const hasTemplateTypeColumn = await knex.schema.hasColumn(TableName.ProjectTemplates, "type");
|
||||||
|
if (hasTemplateTypeColumn) {
|
||||||
|
await knex(TableName.ProjectTemplates).whereNull("type").update({
|
||||||
|
type: ProjectType.SecretManager
|
||||||
|
});
|
||||||
|
await knex.schema.alterTable(TableName.ProjectTemplates, (t) => {
|
||||||
|
t.string("type").notNullable().defaultTo(ProjectType.SecretManager).alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasTypeColumn = await knex.schema.hasColumn(TableName.Project, "type");
|
||||||
|
const hasDefaultTypeColumn = await knex.schema.hasColumn(TableName.Project, "defaultProduct");
|
||||||
|
if (hasTypeColumn && hasDefaultTypeColumn) {
|
||||||
|
await knex(TableName.Project).update({
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore this is because this field is created later
|
||||||
|
type: knex.raw(`"defaultProduct"`)
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Project, (t) => {
|
||||||
|
t.string("type").notNullable().alter();
|
||||||
|
t.string("defaultProduct").nullable().alter();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get all projects that need kickouts in a single query
|
||||||
|
const projectsNeedingKickouts = await getProjectsNeedingKickouts(knex);
|
||||||
|
|
||||||
|
// Process projects in batches to avoid overwhelming the database
|
||||||
|
for (let i = 0; i < projectsNeedingKickouts.length; i += projectsNeedingKickouts.length) {
|
||||||
|
const batch = projectsNeedingKickouts.slice(i, i + BATCH_SIZE);
|
||||||
|
const processedIds: string[] = [];
|
||||||
|
|
||||||
|
for (const project of batch) {
|
||||||
|
const kickoutPromises: Promise<void>[] = [];
|
||||||
|
|
||||||
|
// Only add kickouts that are actually needed (flags are pre-computed)
|
||||||
|
if (project.needsSecretManager) {
|
||||||
|
kickoutPromises.push(kickOutSecretManagerProject(knex, project.id));
|
||||||
|
}
|
||||||
|
if (project.needsCertManager) {
|
||||||
|
kickoutPromises.push(kickOutCertManagerProject(knex, project.id));
|
||||||
|
}
|
||||||
|
if (project.needsKms) {
|
||||||
|
kickoutPromises.push(kickOutKmsProject(knex, project.id));
|
||||||
|
}
|
||||||
|
if (project.needsSsh) {
|
||||||
|
kickoutPromises.push(kickOutSshProject(knex, project.id));
|
||||||
|
}
|
||||||
|
if (project.needsSecretScanning) {
|
||||||
|
kickoutPromises.push(kickOutSecretScanningProject(knex, project.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute all kickouts in parallel and handle any failures gracefully
|
||||||
|
if (kickoutPromises.length > 0) {
|
||||||
|
const results = await Promise.allSettled(kickoutPromises);
|
||||||
|
|
||||||
|
// Log any failures for debugging
|
||||||
|
results.forEach((res) => {
|
||||||
|
if (res.status === "rejected") {
|
||||||
|
throw new Error(`Migration failed for project ${project.id}: ${res.reason}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
processedIds.push(project.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear defaultProduct for the processed batch
|
||||||
|
if (processedIds.length > 0) {
|
||||||
|
await knex(TableName.Project).whereIn("id", processedIds).update("defaultProduct", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await knex.raw(`SET statement_timeout = '${originalTimeout}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasTypeColumn = await knex.schema.hasColumn(TableName.Project, "type");
|
||||||
|
const hasDefaultTypeColumn = await knex.schema.hasColumn(TableName.Project, "defaultProduct");
|
||||||
|
if (hasTypeColumn && hasDefaultTypeColumn) {
|
||||||
|
await knex(TableName.Project).update({
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore this is because this field is created later
|
||||||
|
defaultProduct: knex.raw(`
|
||||||
|
CASE
|
||||||
|
WHEN "type" IS NULL OR "type" = '' THEN 'secret-manager'
|
||||||
|
ELSE "type"
|
||||||
|
END
|
||||||
|
`)
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Project, (t) => {
|
||||||
|
t.string("type").nullable().alter();
|
||||||
|
t.string("defaultProduct").notNullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasTemplateTypeColumn = await knex.schema.hasColumn(TableName.ProjectTemplates, "type");
|
||||||
|
if (hasTemplateTypeColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.ProjectTemplates, (t) => {
|
||||||
|
t.string("type").nullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,96 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { selectAllTableCols } from "@app/lib/knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasTable(TableName.AccessApprovalPolicyEnvironment))) {
|
||||||
|
await knex.schema.createTable(TableName.AccessApprovalPolicyEnvironment, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.uuid("policyId").notNullable();
|
||||||
|
t.foreign("policyId").references("id").inTable(TableName.AccessApprovalPolicy).onDelete("CASCADE");
|
||||||
|
t.uuid("envId").notNullable();
|
||||||
|
t.foreign("envId").references("id").inTable(TableName.Environment);
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
t.unique(["policyId", "envId"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.AccessApprovalPolicyEnvironment);
|
||||||
|
|
||||||
|
const existingAccessApprovalPolicies = await knex(TableName.AccessApprovalPolicy)
|
||||||
|
.select(selectAllTableCols(TableName.AccessApprovalPolicy))
|
||||||
|
.whereNotNull(`${TableName.AccessApprovalPolicy}.envId`);
|
||||||
|
|
||||||
|
const accessApprovalPolicies = existingAccessApprovalPolicies.map(async (policy) => {
|
||||||
|
await knex(TableName.AccessApprovalPolicyEnvironment).insert({
|
||||||
|
policyId: policy.id,
|
||||||
|
envId: policy.envId
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(accessApprovalPolicies);
|
||||||
|
}
|
||||||
|
if (!(await knex.schema.hasTable(TableName.SecretApprovalPolicyEnvironment))) {
|
||||||
|
await knex.schema.createTable(TableName.SecretApprovalPolicyEnvironment, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.uuid("policyId").notNullable();
|
||||||
|
t.foreign("policyId").references("id").inTable(TableName.SecretApprovalPolicy).onDelete("CASCADE");
|
||||||
|
t.uuid("envId").notNullable();
|
||||||
|
t.foreign("envId").references("id").inTable(TableName.Environment);
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
t.unique(["policyId", "envId"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.SecretApprovalPolicyEnvironment);
|
||||||
|
|
||||||
|
const existingSecretApprovalPolicies = await knex(TableName.SecretApprovalPolicy)
|
||||||
|
.select(selectAllTableCols(TableName.SecretApprovalPolicy))
|
||||||
|
.whereNotNull(`${TableName.SecretApprovalPolicy}.envId`);
|
||||||
|
|
||||||
|
const secretApprovalPolicies = existingSecretApprovalPolicies.map(async (policy) => {
|
||||||
|
await knex(TableName.SecretApprovalPolicyEnvironment).insert({
|
||||||
|
policyId: policy.id,
|
||||||
|
envId: policy.envId
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(secretApprovalPolicies);
|
||||||
|
}
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.AccessApprovalPolicy, (t) => {
|
||||||
|
t.dropForeign(["envId"]);
|
||||||
|
|
||||||
|
// Add the new foreign key constraint with ON DELETE SET NULL
|
||||||
|
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("SET NULL");
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalPolicy, (t) => {
|
||||||
|
t.dropForeign(["envId"]);
|
||||||
|
|
||||||
|
// Add the new foreign key constraint with ON DELETE SET NULL
|
||||||
|
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("SET NULL");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasTable(TableName.AccessApprovalPolicyEnvironment)) {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.AccessApprovalPolicyEnvironment);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.AccessApprovalPolicyEnvironment);
|
||||||
|
}
|
||||||
|
if (await knex.schema.hasTable(TableName.SecretApprovalPolicyEnvironment)) {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.SecretApprovalPolicyEnvironment);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.SecretApprovalPolicyEnvironment);
|
||||||
|
}
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.AccessApprovalPolicy, (t) => {
|
||||||
|
t.dropForeign(["envId"]);
|
||||||
|
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalPolicy, (t) => {
|
||||||
|
t.dropForeign(["envId"]);
|
||||||
|
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
|
||||||
|
});
|
||||||
|
}
|
@@ -0,0 +1,111 @@
|
|||||||
|
/* eslint-disable no-await-in-loop */
|
||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { chunkArray } from "@app/lib/fn";
|
||||||
|
import { logger } from "@app/lib/logger";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
import { TReminders, TRemindersInsert } from "../schemas/reminders";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
logger.info("Initializing secret reminders migration");
|
||||||
|
const hasReminderTable = await knex.schema.hasTable(TableName.Reminder);
|
||||||
|
|
||||||
|
if (hasReminderTable) {
|
||||||
|
const secretsWithLatestVersions = await knex(TableName.SecretV2)
|
||||||
|
.whereNotNull(`${TableName.SecretV2}.reminderRepeatDays`)
|
||||||
|
.whereRaw(`"${TableName.SecretV2}"."reminderRepeatDays" > 0`)
|
||||||
|
.innerJoin(TableName.SecretVersionV2, (qb) => {
|
||||||
|
void qb
|
||||||
|
.on(`${TableName.SecretVersionV2}.secretId`, "=", `${TableName.SecretV2}.id`)
|
||||||
|
.andOn(`${TableName.SecretVersionV2}.reminderRepeatDays`, "=", `${TableName.SecretV2}.reminderRepeatDays`);
|
||||||
|
})
|
||||||
|
.whereIn([`${TableName.SecretVersionV2}.secretId`, `${TableName.SecretVersionV2}.version`], (qb) => {
|
||||||
|
void qb
|
||||||
|
.select(["v2.secretId", knex.raw("MIN(v2.version) as version")])
|
||||||
|
.from(`${TableName.SecretVersionV2} as v2`)
|
||||||
|
.innerJoin(`${TableName.SecretV2} as s2`, "v2.secretId", "s2.id")
|
||||||
|
.whereRaw(`v2."reminderRepeatDays" = s2."reminderRepeatDays"`)
|
||||||
|
.whereNotNull("v2.reminderRepeatDays")
|
||||||
|
.whereRaw(`v2."reminderRepeatDays" > 0`)
|
||||||
|
.groupBy("v2.secretId");
|
||||||
|
})
|
||||||
|
// Add LEFT JOIN with Reminder table to check for existing reminders
|
||||||
|
.leftJoin(TableName.Reminder, `${TableName.Reminder}.secretId`, `${TableName.SecretV2}.id`)
|
||||||
|
// Only include secrets that don't already have reminders
|
||||||
|
.whereNull(`${TableName.Reminder}.secretId`)
|
||||||
|
.select(
|
||||||
|
knex.ref("id").withSchema(TableName.SecretV2).as("secretId"),
|
||||||
|
knex.ref("reminderRepeatDays").withSchema(TableName.SecretV2).as("reminderRepeatDays"),
|
||||||
|
knex.ref("reminderNote").withSchema(TableName.SecretV2).as("reminderNote"),
|
||||||
|
knex.ref("createdAt").withSchema(TableName.SecretVersionV2).as("createdAt")
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.info(`Found ${secretsWithLatestVersions.length} reminders to migrate`);
|
||||||
|
|
||||||
|
const reminderInserts: TRemindersInsert[] = [];
|
||||||
|
if (secretsWithLatestVersions.length > 0) {
|
||||||
|
secretsWithLatestVersions.forEach((secret) => {
|
||||||
|
if (!secret.reminderRepeatDays) return;
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const createdAt = new Date(secret.createdAt);
|
||||||
|
let nextReminderDate = new Date(createdAt);
|
||||||
|
nextReminderDate.setDate(nextReminderDate.getDate() + secret.reminderRepeatDays);
|
||||||
|
|
||||||
|
// If the next reminder date is in the past, calculate the proper next occurrence
|
||||||
|
if (nextReminderDate < now) {
|
||||||
|
const daysSinceCreation = Math.floor((now.getTime() - createdAt.getTime()) / (1000 * 60 * 60 * 24));
|
||||||
|
const daysIntoCurrentCycle = daysSinceCreation % secret.reminderRepeatDays;
|
||||||
|
const daysUntilNextReminder = secret.reminderRepeatDays - daysIntoCurrentCycle;
|
||||||
|
|
||||||
|
nextReminderDate = new Date(now);
|
||||||
|
nextReminderDate.setDate(now.getDate() + daysUntilNextReminder);
|
||||||
|
}
|
||||||
|
|
||||||
|
reminderInserts.push({
|
||||||
|
secretId: secret.secretId,
|
||||||
|
message: secret.reminderNote,
|
||||||
|
repeatDays: secret.reminderRepeatDays,
|
||||||
|
nextReminderDate
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const commitBatches = chunkArray(reminderInserts, 2000);
|
||||||
|
for (const commitBatch of commitBatches) {
|
||||||
|
const insertedReminders = (await knex
|
||||||
|
.batchInsert(TableName.Reminder, commitBatch)
|
||||||
|
.returning("*")) as TReminders[];
|
||||||
|
|
||||||
|
const insertedReminderSecretIds = insertedReminders.map((reminder) => reminder.secretId).filter(Boolean);
|
||||||
|
|
||||||
|
const recipients = await knex(TableName.SecretReminderRecipients)
|
||||||
|
.whereRaw(`??.?? IN (${insertedReminderSecretIds.map(() => "?").join(",")})`, [
|
||||||
|
TableName.SecretReminderRecipients,
|
||||||
|
"secretId",
|
||||||
|
...insertedReminderSecretIds
|
||||||
|
])
|
||||||
|
.select(
|
||||||
|
knex.ref("userId").withSchema(TableName.SecretReminderRecipients).as("userId"),
|
||||||
|
knex.ref("secretId").withSchema(TableName.SecretReminderRecipients).as("secretId")
|
||||||
|
);
|
||||||
|
const reminderRecipients = recipients.map((recipient) => ({
|
||||||
|
reminderId: insertedReminders.find((reminder) => reminder.secretId === recipient.secretId)?.id,
|
||||||
|
userId: recipient.userId
|
||||||
|
}));
|
||||||
|
|
||||||
|
const filteredRecipients = reminderRecipients.filter((recipient) => Boolean(recipient.reminderId));
|
||||||
|
await knex.batchInsert(TableName.ReminderRecipient, filteredRecipients);
|
||||||
|
}
|
||||||
|
logger.info(`Successfully migrated ${reminderInserts.length} secret reminders`);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Secret reminders migration completed");
|
||||||
|
} else {
|
||||||
|
logger.warn("Reminder table does not exist, skipping migration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(): Promise<void> {
|
||||||
|
logger.info("Rollback not implemented for secret reminders fix migration");
|
||||||
|
}
|
@@ -53,7 +53,7 @@ export const getMigrationEnvConfig = async (superAdminDAL: TSuperAdminDALFactory
|
|||||||
|
|
||||||
let envCfg = Object.freeze(parsedEnv.data);
|
let envCfg = Object.freeze(parsedEnv.data);
|
||||||
|
|
||||||
const fipsEnabled = await crypto.initialize(superAdminDAL);
|
const fipsEnabled = await crypto.initialize(superAdminDAL, envCfg);
|
||||||
|
|
||||||
// Fix for 128-bit entropy encryption key expansion issue:
|
// Fix for 128-bit entropy encryption key expansion issue:
|
||||||
// In FIPS it is not ideal to expand a 128-bit key into 256-bit. We solved this issue in the past by creating the ROOT_ENCRYPTION_KEY.
|
// In FIPS it is not ideal to expand a 128-bit key into 256-bit. We solved this issue in the past by creating the ROOT_ENCRYPTION_KEY.
|
||||||
|
@@ -0,0 +1,25 @@
|
|||||||
|
// Code generated by automation script, DO NOT EDIT.
|
||||||
|
// Automated by pulling database and generating zod schema
|
||||||
|
// To update. Just run npm run generate:schema
|
||||||
|
// Written by akhilmhdh.
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const AccessApprovalPoliciesEnvironmentsSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
policyId: z.string().uuid(),
|
||||||
|
envId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TAccessApprovalPoliciesEnvironments = z.infer<typeof AccessApprovalPoliciesEnvironmentsSchema>;
|
||||||
|
export type TAccessApprovalPoliciesEnvironmentsInsert = Omit<
|
||||||
|
z.input<typeof AccessApprovalPoliciesEnvironmentsSchema>,
|
||||||
|
TImmutableDBKeys
|
||||||
|
>;
|
||||||
|
export type TAccessApprovalPoliciesEnvironmentsUpdate = Partial<
|
||||||
|
Omit<z.input<typeof AccessApprovalPoliciesEnvironmentsSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
@@ -100,6 +100,7 @@ export enum TableName {
|
|||||||
AccessApprovalPolicyBypasser = "access_approval_policies_bypassers",
|
AccessApprovalPolicyBypasser = "access_approval_policies_bypassers",
|
||||||
AccessApprovalRequest = "access_approval_requests",
|
AccessApprovalRequest = "access_approval_requests",
|
||||||
AccessApprovalRequestReviewer = "access_approval_requests_reviewers",
|
AccessApprovalRequestReviewer = "access_approval_requests_reviewers",
|
||||||
|
AccessApprovalPolicyEnvironment = "access_approval_policies_environments",
|
||||||
SecretApprovalPolicy = "secret_approval_policies",
|
SecretApprovalPolicy = "secret_approval_policies",
|
||||||
SecretApprovalPolicyApprover = "secret_approval_policies_approvers",
|
SecretApprovalPolicyApprover = "secret_approval_policies_approvers",
|
||||||
SecretApprovalPolicyBypasser = "secret_approval_policies_bypassers",
|
SecretApprovalPolicyBypasser = "secret_approval_policies_bypassers",
|
||||||
@@ -107,6 +108,7 @@ export enum TableName {
|
|||||||
SecretApprovalRequestReviewer = "secret_approval_requests_reviewers",
|
SecretApprovalRequestReviewer = "secret_approval_requests_reviewers",
|
||||||
SecretApprovalRequestSecret = "secret_approval_requests_secrets",
|
SecretApprovalRequestSecret = "secret_approval_requests_secrets",
|
||||||
SecretApprovalRequestSecretTag = "secret_approval_request_secret_tags",
|
SecretApprovalRequestSecretTag = "secret_approval_request_secret_tags",
|
||||||
|
SecretApprovalPolicyEnvironment = "secret_approval_policies_environments",
|
||||||
SecretRotation = "secret_rotations",
|
SecretRotation = "secret_rotations",
|
||||||
SecretRotationOutput = "secret_rotation_outputs",
|
SecretRotationOutput = "secret_rotation_outputs",
|
||||||
SamlConfig = "saml_configs",
|
SamlConfig = "saml_configs",
|
||||||
@@ -160,7 +162,7 @@ export enum TableName {
|
|||||||
SecretRotationV2SecretMapping = "secret_rotation_v2_secret_mappings",
|
SecretRotationV2SecretMapping = "secret_rotation_v2_secret_mappings",
|
||||||
MicrosoftTeamsIntegrations = "microsoft_teams_integrations",
|
MicrosoftTeamsIntegrations = "microsoft_teams_integrations",
|
||||||
ProjectMicrosoftTeamsConfigs = "project_microsoft_teams_configs",
|
ProjectMicrosoftTeamsConfigs = "project_microsoft_teams_configs",
|
||||||
SecretReminderRecipients = "secret_reminder_recipients",
|
SecretReminderRecipients = "secret_reminder_recipients", // TODO(Carlos): Remove this in the future after migrating to the new reminder recipients table
|
||||||
GithubOrgSyncConfig = "github_org_sync_configs",
|
GithubOrgSyncConfig = "github_org_sync_configs",
|
||||||
FolderCommit = "folder_commits",
|
FolderCommit = "folder_commits",
|
||||||
FolderCommitChanges = "folder_commit_changes",
|
FolderCommitChanges = "folder_commit_changes",
|
||||||
@@ -172,7 +174,10 @@ export enum TableName {
|
|||||||
SecretScanningResource = "secret_scanning_resources",
|
SecretScanningResource = "secret_scanning_resources",
|
||||||
SecretScanningScan = "secret_scanning_scans",
|
SecretScanningScan = "secret_scanning_scans",
|
||||||
SecretScanningFinding = "secret_scanning_findings",
|
SecretScanningFinding = "secret_scanning_findings",
|
||||||
SecretScanningConfig = "secret_scanning_configs"
|
SecretScanningConfig = "secret_scanning_configs",
|
||||||
|
// reminders
|
||||||
|
Reminder = "reminders",
|
||||||
|
ReminderRecipient = "reminders_recipients"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt" | "commitId";
|
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt" | "commitId";
|
||||||
@@ -267,6 +272,16 @@ export enum ProjectType {
|
|||||||
SecretScanning = "secret-scanning"
|
SecretScanning = "secret-scanning"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ActionProjectType {
|
||||||
|
SecretManager = ProjectType.SecretManager,
|
||||||
|
CertificateManager = ProjectType.CertificateManager,
|
||||||
|
KMS = ProjectType.KMS,
|
||||||
|
SSH = ProjectType.SSH,
|
||||||
|
SecretScanning = ProjectType.SecretScanning,
|
||||||
|
// project operations that happen on all types
|
||||||
|
Any = "any"
|
||||||
|
}
|
||||||
|
|
||||||
export enum SortDirection {
|
export enum SortDirection {
|
||||||
ASC = "asc",
|
ASC = "asc",
|
||||||
DESC = "desc"
|
DESC = "desc"
|
||||||
|
@@ -16,7 +16,7 @@ export const ProjectTemplatesSchema = z.object({
|
|||||||
orgId: z.string().uuid(),
|
orgId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
type: z.string().nullable().optional()
|
type: z.string().default("secret-manager")
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TProjectTemplates = z.infer<typeof ProjectTemplatesSchema>;
|
export type TProjectTemplates = z.infer<typeof ProjectTemplatesSchema>;
|
||||||
|
@@ -25,12 +25,12 @@ export const ProjectsSchema = z.object({
|
|||||||
kmsSecretManagerKeyId: z.string().uuid().nullable().optional(),
|
kmsSecretManagerKeyId: z.string().uuid().nullable().optional(),
|
||||||
kmsSecretManagerEncryptedDataKey: zodBuffer.nullable().optional(),
|
kmsSecretManagerEncryptedDataKey: zodBuffer.nullable().optional(),
|
||||||
description: z.string().nullable().optional(),
|
description: z.string().nullable().optional(),
|
||||||
type: z.string().nullable().optional(),
|
type: z.string(),
|
||||||
enforceCapitalization: z.boolean().default(false),
|
enforceCapitalization: z.boolean().default(false),
|
||||||
hasDeleteProtection: z.boolean().default(false).nullable().optional(),
|
hasDeleteProtection: z.boolean().default(false).nullable().optional(),
|
||||||
secretSharing: z.boolean().default(true),
|
secretSharing: z.boolean().default(true),
|
||||||
showSnapshotsLegacy: z.boolean().default(false),
|
showSnapshotsLegacy: z.boolean().default(false),
|
||||||
defaultProduct: z.string().default("secret-manager")
|
defaultProduct: z.string().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TProjects = z.infer<typeof ProjectsSchema>;
|
export type TProjects = z.infer<typeof ProjectsSchema>;
|
||||||
|
20
backend/src/db/schemas/reminders-recipients.ts
Normal file
20
backend/src/db/schemas/reminders-recipients.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// 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 RemindersRecipientsSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
reminderId: z.string().uuid(),
|
||||||
|
userId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TRemindersRecipients = z.infer<typeof RemindersRecipientsSchema>;
|
||||||
|
export type TRemindersRecipientsInsert = Omit<z.input<typeof RemindersRecipientsSchema>, TImmutableDBKeys>;
|
||||||
|
export type TRemindersRecipientsUpdate = Partial<Omit<z.input<typeof RemindersRecipientsSchema>, TImmutableDBKeys>>;
|
22
backend/src/db/schemas/reminders.ts
Normal file
22
backend/src/db/schemas/reminders.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// 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 RemindersSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
secretId: z.string().uuid().nullable().optional(),
|
||||||
|
message: z.string().nullable().optional(),
|
||||||
|
repeatDays: z.number().nullable().optional(),
|
||||||
|
nextReminderDate: z.date(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TReminders = z.infer<typeof RemindersSchema>;
|
||||||
|
export type TRemindersInsert = Omit<z.input<typeof RemindersSchema>, TImmutableDBKeys>;
|
||||||
|
export type TRemindersUpdate = Partial<Omit<z.input<typeof RemindersSchema>, TImmutableDBKeys>>;
|
@@ -0,0 +1,25 @@
|
|||||||
|
// Code generated by automation script, DO NOT EDIT.
|
||||||
|
// Automated by pulling database and generating zod schema
|
||||||
|
// To update. Just run npm run generate:schema
|
||||||
|
// Written by akhilmhdh.
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const SecretApprovalPoliciesEnvironmentsSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
policyId: z.string().uuid(),
|
||||||
|
envId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TSecretApprovalPoliciesEnvironments = z.infer<typeof SecretApprovalPoliciesEnvironmentsSchema>;
|
||||||
|
export type TSecretApprovalPoliciesEnvironmentsInsert = Omit<
|
||||||
|
z.input<typeof SecretApprovalPoliciesEnvironmentsSchema>,
|
||||||
|
TImmutableDBKeys
|
||||||
|
>;
|
||||||
|
export type TSecretApprovalPoliciesEnvironmentsUpdate = Partial<
|
||||||
|
Omit<z.input<typeof SecretApprovalPoliciesEnvironmentsSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
@@ -17,52 +17,66 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
rateLimit: writeLimit
|
rateLimit: writeLimit
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z
|
||||||
projectSlug: z.string().trim(),
|
.object({
|
||||||
name: z.string().optional(),
|
projectSlug: z.string().trim(),
|
||||||
secretPath: z.string().trim().min(1, { message: "Secret path cannot be empty" }).transform(removeTrailingSlash),
|
name: z.string().optional(),
|
||||||
environment: z.string(),
|
secretPath: z
|
||||||
approvers: z
|
.string()
|
||||||
.discriminatedUnion("type", [
|
.trim()
|
||||||
z.object({
|
.min(1, { message: "Secret path cannot be empty" })
|
||||||
type: z.literal(ApproverType.Group),
|
.transform(removeTrailingSlash),
|
||||||
id: z.string(),
|
environment: z.string().optional(),
|
||||||
sequence: z.number().int().default(1)
|
environments: z.string().array().optional(),
|
||||||
}),
|
approvers: z
|
||||||
z.object({
|
.discriminatedUnion("type", [
|
||||||
type: z.literal(ApproverType.User),
|
z.object({
|
||||||
id: z.string().optional(),
|
type: z.literal(ApproverType.Group),
|
||||||
username: z.string().optional(),
|
id: z.string(),
|
||||||
sequence: z.number().int().default(1)
|
sequence: z.number().int().default(1)
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
type: z.literal(ApproverType.User),
|
||||||
|
id: z.string().optional(),
|
||||||
|
username: z.string().optional(),
|
||||||
|
sequence: z.number().int().default(1)
|
||||||
|
})
|
||||||
|
])
|
||||||
|
.array()
|
||||||
|
.max(100, "Cannot have more than 100 approvers")
|
||||||
|
.min(1, { message: "At least one approver should be provided" })
|
||||||
|
.refine(
|
||||||
|
// @ts-expect-error this is ok
|
||||||
|
(el) => el.every((i) => Boolean(i?.id) || Boolean(i?.username)),
|
||||||
|
"Must provide either username or id"
|
||||||
|
),
|
||||||
|
bypassers: z
|
||||||
|
.discriminatedUnion("type", [
|
||||||
|
z.object({ type: z.literal(BypasserType.Group), id: z.string() }),
|
||||||
|
z.object({
|
||||||
|
type: z.literal(BypasserType.User),
|
||||||
|
id: z.string().optional(),
|
||||||
|
username: z.string().optional()
|
||||||
|
})
|
||||||
|
])
|
||||||
|
.array()
|
||||||
|
.max(100, "Cannot have more than 100 bypassers")
|
||||||
|
.optional(),
|
||||||
|
approvalsRequired: z
|
||||||
|
.object({
|
||||||
|
numberOfApprovals: z.number().int(),
|
||||||
|
stepNumber: z.number().int()
|
||||||
})
|
})
|
||||||
])
|
.array()
|
||||||
.array()
|
.optional(),
|
||||||
.max(100, "Cannot have more than 100 approvers")
|
approvals: z.number().min(1).default(1),
|
||||||
.min(1, { message: "At least one approver should be provided" })
|
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
||||||
.refine(
|
allowedSelfApprovals: z.boolean().default(true)
|
||||||
// @ts-expect-error this is ok
|
})
|
||||||
(el) => el.every((i) => Boolean(i?.id) || Boolean(i?.username)),
|
.refine(
|
||||||
"Must provide either username or id"
|
(val) => Boolean(val.environment) || Boolean(val.environments),
|
||||||
),
|
"Must provide either environment or environments"
|
||||||
bypassers: z
|
),
|
||||||
.discriminatedUnion("type", [
|
|
||||||
z.object({ type: z.literal(BypasserType.Group), id: z.string() }),
|
|
||||||
z.object({ type: z.literal(BypasserType.User), id: z.string().optional(), username: z.string().optional() })
|
|
||||||
])
|
|
||||||
.array()
|
|
||||||
.max(100, "Cannot have more than 100 bypassers")
|
|
||||||
.optional(),
|
|
||||||
approvalsRequired: z
|
|
||||||
.object({
|
|
||||||
numberOfApprovals: z.number().int(),
|
|
||||||
stepNumber: z.number().int()
|
|
||||||
})
|
|
||||||
.array()
|
|
||||||
.optional(),
|
|
||||||
approvals: z.number().min(1).default(1),
|
|
||||||
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
|
||||||
allowedSelfApprovals: z.boolean().default(true)
|
|
||||||
}),
|
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
approval: sapPubSchema
|
approval: sapPubSchema
|
||||||
@@ -78,7 +92,8 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
...req.body,
|
...req.body,
|
||||||
projectSlug: req.body.projectSlug,
|
projectSlug: req.body.projectSlug,
|
||||||
name: req.body.name ?? `${req.body.environment}-${nanoid(3)}`,
|
name:
|
||||||
|
req.body.name ?? `${req.body.environment || req.body.environments?.join("-").substring(0, 250)}-${nanoid(3)}`,
|
||||||
enforcementLevel: req.body.enforcementLevel
|
enforcementLevel: req.body.enforcementLevel
|
||||||
});
|
});
|
||||||
return { approval };
|
return { approval };
|
||||||
@@ -211,6 +226,7 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
approvals: z.number().min(1).optional(),
|
approvals: z.number().min(1).optional(),
|
||||||
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
||||||
allowedSelfApprovals: z.boolean().default(true),
|
allowedSelfApprovals: z.boolean().default(true),
|
||||||
|
environments: z.array(z.string()).optional(),
|
||||||
approvalsRequired: z
|
approvalsRequired: z
|
||||||
.object({
|
.object({
|
||||||
numberOfApprovals: z.number().int(),
|
numberOfApprovals: z.number().int(),
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
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 { isInfisicalProjectTemplate } from "@app/ee/services/project-template/project-template-fns";
|
import { isInfisicalProjectTemplate } from "@app/ee/services/project-template/project-template-fns";
|
||||||
@@ -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,10 @@ 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 projectTemplates = await server.services.projectTemplate.listProjectTemplatesByOrg(
|
||||||
|
req.permission,
|
||||||
|
req.query.type
|
||||||
|
);
|
||||||
|
|
||||||
const auditTemplates = projectTemplates.filter((template) => !isInfisicalProjectTemplate(template.name));
|
const auditTemplates = projectTemplates.filter((template) => !isInfisicalProjectTemplate(template.name));
|
||||||
|
|
||||||
@@ -191,6 +197,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),
|
||||||
|
type: z.nativeEnum(ProjectType).describe(ProjectTemplates.CREATE.type),
|
||||||
environments: ProjectTemplateEnvironmentsSchema.describe(ProjectTemplates.CREATE.environments).optional()
|
environments: ProjectTemplateEnvironmentsSchema.describe(ProjectTemplates.CREATE.environments).optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
|
@@ -17,34 +17,45 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
rateLimit: writeLimit
|
rateLimit: writeLimit
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z
|
||||||
workspaceId: z.string(),
|
.object({
|
||||||
name: z.string().optional(),
|
workspaceId: z.string(),
|
||||||
environment: z.string(),
|
name: z.string().optional(),
|
||||||
secretPath: z
|
environment: z.string().optional(),
|
||||||
.string()
|
environments: z.string().array().optional(),
|
||||||
.min(1, { message: "Secret path cannot be empty" })
|
secretPath: z
|
||||||
.transform((val) => removeTrailingSlash(val)),
|
.string()
|
||||||
approvers: z
|
.min(1, { message: "Secret path cannot be empty" })
|
||||||
.discriminatedUnion("type", [
|
.transform((val) => removeTrailingSlash(val)),
|
||||||
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
|
approvers: z
|
||||||
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), username: z.string().optional() })
|
.discriminatedUnion("type", [
|
||||||
])
|
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
|
||||||
.array()
|
z.object({
|
||||||
.min(1, { message: "At least one approver should be provided" })
|
type: z.literal(ApproverType.User),
|
||||||
.max(100, "Cannot have more than 100 approvers"),
|
id: z.string().optional(),
|
||||||
bypassers: z
|
username: z.string().optional()
|
||||||
.discriminatedUnion("type", [
|
})
|
||||||
z.object({ type: z.literal(BypasserType.Group), id: z.string() }),
|
])
|
||||||
z.object({ type: z.literal(BypasserType.User), id: z.string().optional(), username: z.string().optional() })
|
.array()
|
||||||
])
|
.min(1, { message: "At least one approver should be provided" })
|
||||||
.array()
|
.max(100, "Cannot have more than 100 approvers"),
|
||||||
.max(100, "Cannot have more than 100 bypassers")
|
bypassers: z
|
||||||
.optional(),
|
.discriminatedUnion("type", [
|
||||||
approvals: z.number().min(1).default(1),
|
z.object({ type: z.literal(BypasserType.Group), id: z.string() }),
|
||||||
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
z.object({
|
||||||
allowedSelfApprovals: z.boolean().default(true)
|
type: z.literal(BypasserType.User),
|
||||||
}),
|
id: z.string().optional(),
|
||||||
|
username: z.string().optional()
|
||||||
|
})
|
||||||
|
])
|
||||||
|
.array()
|
||||||
|
.max(100, "Cannot have more than 100 bypassers")
|
||||||
|
.optional(),
|
||||||
|
approvals: z.number().min(1).default(1),
|
||||||
|
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
||||||
|
allowedSelfApprovals: z.boolean().default(true)
|
||||||
|
})
|
||||||
|
.refine((data) => data.environment || data.environments, "At least one environment should be provided"),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
approval: sapPubSchema
|
approval: sapPubSchema
|
||||||
@@ -60,7 +71,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
projectId: req.body.workspaceId,
|
projectId: req.body.workspaceId,
|
||||||
...req.body,
|
...req.body,
|
||||||
name: req.body.name ?? `${req.body.environment}-${nanoid(3)}`,
|
name: req.body.name ?? `${req.body.environment || req.body.environments?.join(",")}-${nanoid(3)}`,
|
||||||
enforcementLevel: req.body.enforcementLevel
|
enforcementLevel: req.body.enforcementLevel
|
||||||
});
|
});
|
||||||
return { approval };
|
return { approval };
|
||||||
@@ -103,7 +114,8 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
.optional()
|
.optional()
|
||||||
.transform((val) => (val ? removeTrailingSlash(val) : undefined)),
|
.transform((val) => (val ? removeTrailingSlash(val) : undefined)),
|
||||||
enforcementLevel: z.nativeEnum(EnforcementLevel).optional(),
|
enforcementLevel: z.nativeEnum(EnforcementLevel).optional(),
|
||||||
allowedSelfApprovals: z.boolean().default(true)
|
allowedSelfApprovals: z.boolean().default(true),
|
||||||
|
environments: z.array(z.string()).optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@@ -315,10 +315,12 @@ export const registerSecretRotationEndpoints = <
|
|||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
deleteSecrets: z
|
deleteSecrets: z
|
||||||
.enum(["true", "false"])
|
.enum(["true", "false"])
|
||||||
|
.optional()
|
||||||
.transform((value) => value === "true")
|
.transform((value) => value === "true")
|
||||||
.describe(SecretRotations.DELETE(type).deleteSecrets),
|
.describe(SecretRotations.DELETE(type).deleteSecrets),
|
||||||
revokeGeneratedCredentials: z
|
revokeGeneratedCredentials: z
|
||||||
.enum(["true", "false"])
|
.enum(["true", "false"])
|
||||||
|
.optional()
|
||||||
.transform((value) => value === "true")
|
.transform((value) => value === "true")
|
||||||
.describe(SecretRotations.DELETE(type).revokeGeneratedCredentials)
|
.describe(SecretRotations.DELETE(type).revokeGeneratedCredentials)
|
||||||
}),
|
}),
|
||||||
|
@@ -26,6 +26,7 @@ export interface TAccessApprovalPolicyDALFactory
|
|||||||
>,
|
>,
|
||||||
customFilter?: {
|
customFilter?: {
|
||||||
policyId?: string;
|
policyId?: string;
|
||||||
|
envId?: string;
|
||||||
},
|
},
|
||||||
tx?: Knex
|
tx?: Knex
|
||||||
) => Promise<
|
) => Promise<
|
||||||
@@ -55,11 +56,6 @@ export interface TAccessApprovalPolicyDALFactory
|
|||||||
allowedSelfApprovals: boolean;
|
allowedSelfApprovals: boolean;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
deletedAt?: Date | null | undefined;
|
deletedAt?: Date | null | undefined;
|
||||||
environment: {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
};
|
|
||||||
projectId: string;
|
projectId: string;
|
||||||
bypassers: (
|
bypassers: (
|
||||||
| {
|
| {
|
||||||
@@ -72,6 +68,11 @@ export interface TAccessApprovalPolicyDALFactory
|
|||||||
type: BypasserType.Group;
|
type: BypasserType.Group;
|
||||||
}
|
}
|
||||||
)[];
|
)[];
|
||||||
|
environments: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
}[];
|
||||||
}[]
|
}[]
|
||||||
>;
|
>;
|
||||||
findById: (
|
findById: (
|
||||||
@@ -95,11 +96,11 @@ export interface TAccessApprovalPolicyDALFactory
|
|||||||
allowedSelfApprovals: boolean;
|
allowedSelfApprovals: boolean;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
deletedAt?: Date | null | undefined;
|
deletedAt?: Date | null | undefined;
|
||||||
environment: {
|
environments: {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
};
|
}[];
|
||||||
projectId: string;
|
projectId: string;
|
||||||
}
|
}
|
||||||
| undefined
|
| undefined
|
||||||
@@ -143,6 +144,26 @@ export interface TAccessApprovalPolicyDALFactory
|
|||||||
}
|
}
|
||||||
| undefined
|
| undefined
|
||||||
>;
|
>;
|
||||||
|
findPolicyByEnvIdAndSecretPath: (
|
||||||
|
{ envIds, secretPath }: { envIds: string[]; secretPath: string },
|
||||||
|
tx?: Knex
|
||||||
|
) => Promise<{
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
approvals: number;
|
||||||
|
enforcementLevel: string;
|
||||||
|
allowedSelfApprovals: boolean;
|
||||||
|
secretPath: string;
|
||||||
|
deletedAt?: Date | null | undefined;
|
||||||
|
environments: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
}[];
|
||||||
|
projectId: string;
|
||||||
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TAccessApprovalPolicyServiceFactory {
|
export interface TAccessApprovalPolicyServiceFactory {
|
||||||
@@ -367,6 +388,7 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient): TAccessApprovalPo
|
|||||||
filter: TFindFilter<TAccessApprovalPolicies & { projectId: string }>,
|
filter: TFindFilter<TAccessApprovalPolicies & { projectId: string }>,
|
||||||
customFilter?: {
|
customFilter?: {
|
||||||
policyId?: string;
|
policyId?: string;
|
||||||
|
envId?: string;
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
const result = await tx(TableName.AccessApprovalPolicy)
|
const result = await tx(TableName.AccessApprovalPolicy)
|
||||||
@@ -377,7 +399,17 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient): TAccessApprovalPo
|
|||||||
void qb.where(`${TableName.AccessApprovalPolicy}.id`, "=", customFilter.policyId);
|
void qb.where(`${TableName.AccessApprovalPolicy}.id`, "=", customFilter.policyId);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.join(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
.join(
|
||||||
|
TableName.AccessApprovalPolicyEnvironment,
|
||||||
|
`${TableName.AccessApprovalPolicy}.id`,
|
||||||
|
`${TableName.AccessApprovalPolicyEnvironment}.policyId`
|
||||||
|
)
|
||||||
|
.join(TableName.Environment, `${TableName.AccessApprovalPolicyEnvironment}.envId`, `${TableName.Environment}.id`)
|
||||||
|
.where((qb) => {
|
||||||
|
if (customFilter?.envId) {
|
||||||
|
void qb.where(`${TableName.AccessApprovalPolicyEnvironment}.envId`, "=", customFilter.envId);
|
||||||
|
}
|
||||||
|
})
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.AccessApprovalPolicyApprover,
|
TableName.AccessApprovalPolicyApprover,
|
||||||
`${TableName.AccessApprovalPolicy}.id`,
|
`${TableName.AccessApprovalPolicy}.id`,
|
||||||
@@ -404,7 +436,7 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient): TAccessApprovalPo
|
|||||||
.select(tx.ref("bypasserGroupId").withSchema(TableName.AccessApprovalPolicyBypasser))
|
.select(tx.ref("bypasserGroupId").withSchema(TableName.AccessApprovalPolicyBypasser))
|
||||||
.select(tx.ref("name").withSchema(TableName.Environment).as("envName"))
|
.select(tx.ref("name").withSchema(TableName.Environment).as("envName"))
|
||||||
.select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
.select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||||
.select(tx.ref("id").withSchema(TableName.Environment).as("envId"))
|
.select(tx.ref("id").withSchema(TableName.Environment).as("environmentId"))
|
||||||
.select(tx.ref("projectId").withSchema(TableName.Environment))
|
.select(tx.ref("projectId").withSchema(TableName.Environment))
|
||||||
.select(selectAllTableCols(TableName.AccessApprovalPolicy));
|
.select(selectAllTableCols(TableName.AccessApprovalPolicy));
|
||||||
|
|
||||||
@@ -448,6 +480,15 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient): TAccessApprovalPo
|
|||||||
sequence: approverSequence,
|
sequence: approverSequence,
|
||||||
approvalsRequired
|
approvalsRequired
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "environmentId",
|
||||||
|
label: "environments" as const,
|
||||||
|
mapper: ({ environmentId: id, envName, envSlug }) => ({
|
||||||
|
id,
|
||||||
|
name: envName,
|
||||||
|
slug: envSlug
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -470,11 +511,6 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient): TAccessApprovalPo
|
|||||||
data: docs,
|
data: docs,
|
||||||
key: "id",
|
key: "id",
|
||||||
parentMapper: (data) => ({
|
parentMapper: (data) => ({
|
||||||
environment: {
|
|
||||||
id: data.envId,
|
|
||||||
name: data.envName,
|
|
||||||
slug: data.envSlug
|
|
||||||
},
|
|
||||||
projectId: data.projectId,
|
projectId: data.projectId,
|
||||||
...AccessApprovalPoliciesSchema.parse(data)
|
...AccessApprovalPoliciesSchema.parse(data)
|
||||||
// secretPath: data.secretPath || undefined,
|
// secretPath: data.secretPath || undefined,
|
||||||
@@ -517,6 +553,15 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient): TAccessApprovalPo
|
|||||||
id,
|
id,
|
||||||
type: BypasserType.Group as const
|
type: BypasserType.Group as const
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "environmentId",
|
||||||
|
label: "environments" as const,
|
||||||
|
mapper: ({ environmentId: id, envName, envSlug }) => ({
|
||||||
|
id,
|
||||||
|
name: envName,
|
||||||
|
slug: envSlug
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -545,14 +590,20 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient): TAccessApprovalPo
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
buildFindFilter(
|
buildFindFilter(
|
||||||
{
|
{
|
||||||
envId,
|
|
||||||
secretPath
|
secretPath
|
||||||
},
|
},
|
||||||
TableName.AccessApprovalPolicy
|
TableName.AccessApprovalPolicy
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.join(
|
||||||
|
TableName.AccessApprovalPolicyEnvironment,
|
||||||
|
`${TableName.AccessApprovalPolicyEnvironment}.policyId`,
|
||||||
|
`${TableName.AccessApprovalPolicy}.id`
|
||||||
|
)
|
||||||
|
.where(`${TableName.AccessApprovalPolicyEnvironment}.envId`, "=", envId)
|
||||||
.orderBy("deletedAt", "desc")
|
.orderBy("deletedAt", "desc")
|
||||||
.orderByRaw(`"deletedAt" IS NULL`)
|
.orderByRaw(`"deletedAt" IS NULL`)
|
||||||
|
.select(selectAllTableCols(TableName.AccessApprovalPolicy))
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -561,5 +612,81 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient): TAccessApprovalPo
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { ...accessApprovalPolicyOrm, find, findById, softDeleteById, findLastValidPolicy };
|
const findPolicyByEnvIdAndSecretPath: TAccessApprovalPolicyDALFactory["findPolicyByEnvIdAndSecretPath"] = async (
|
||||||
|
{ envIds, secretPath },
|
||||||
|
tx
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const docs = await (tx || db.replicaNode())(TableName.AccessApprovalPolicy)
|
||||||
|
.join(
|
||||||
|
TableName.AccessApprovalPolicyEnvironment,
|
||||||
|
`${TableName.AccessApprovalPolicyEnvironment}.policyId`,
|
||||||
|
`${TableName.AccessApprovalPolicy}.id`
|
||||||
|
)
|
||||||
|
.join(
|
||||||
|
TableName.Environment,
|
||||||
|
`${TableName.AccessApprovalPolicyEnvironment}.envId`,
|
||||||
|
`${TableName.Environment}.id`
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
buildFindFilter(
|
||||||
|
{
|
||||||
|
$in: {
|
||||||
|
envId: envIds
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TableName.AccessApprovalPolicyEnvironment
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
buildFindFilter(
|
||||||
|
{
|
||||||
|
secretPath
|
||||||
|
},
|
||||||
|
TableName.AccessApprovalPolicy
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.whereNull(`${TableName.AccessApprovalPolicy}.deletedAt`)
|
||||||
|
.orderBy("deletedAt", "desc")
|
||||||
|
.orderByRaw(`"deletedAt" IS NULL`)
|
||||||
|
.select(selectAllTableCols(TableName.AccessApprovalPolicy))
|
||||||
|
.select(db.ref("name").withSchema(TableName.Environment).as("envName"))
|
||||||
|
.select(db.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||||
|
.select(db.ref("id").withSchema(TableName.Environment).as("environmentId"))
|
||||||
|
.select(db.ref("projectId").withSchema(TableName.Environment));
|
||||||
|
const formattedDocs = sqlNestRelationships({
|
||||||
|
data: docs,
|
||||||
|
key: "id",
|
||||||
|
parentMapper: (data) => ({
|
||||||
|
projectId: data.projectId,
|
||||||
|
...AccessApprovalPoliciesSchema.parse(data)
|
||||||
|
}),
|
||||||
|
childrenMapper: [
|
||||||
|
{
|
||||||
|
key: "environmentId",
|
||||||
|
label: "environments" as const,
|
||||||
|
mapper: ({ environmentId: id, envName, envSlug }) => ({
|
||||||
|
id,
|
||||||
|
name: envName,
|
||||||
|
slug: envSlug
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
return formattedDocs?.[0];
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "findPolicyByEnvIdAndSecretPath" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...accessApprovalPolicyOrm,
|
||||||
|
find,
|
||||||
|
findById,
|
||||||
|
softDeleteById,
|
||||||
|
findLastValidPolicy,
|
||||||
|
findPolicyByEnvIdAndSecretPath
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@@ -0,0 +1,32 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TDbClient } from "@app/db";
|
||||||
|
import { TableName } from "@app/db/schemas";
|
||||||
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
|
import { buildFindFilter, ormify, selectAllTableCols } from "@app/lib/knex";
|
||||||
|
|
||||||
|
export type TAccessApprovalPolicyEnvironmentDALFactory = ReturnType<typeof accessApprovalPolicyEnvironmentDALFactory>;
|
||||||
|
|
||||||
|
export const accessApprovalPolicyEnvironmentDALFactory = (db: TDbClient) => {
|
||||||
|
const accessApprovalPolicyEnvironmentOrm = ormify(db, TableName.AccessApprovalPolicyEnvironment);
|
||||||
|
|
||||||
|
const findAvailablePoliciesByEnvId = async (envId: string, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const docs = await (tx || db.replicaNode())(TableName.AccessApprovalPolicyEnvironment)
|
||||||
|
.join(
|
||||||
|
TableName.AccessApprovalPolicy,
|
||||||
|
`${TableName.AccessApprovalPolicyEnvironment}.policyId`,
|
||||||
|
`${TableName.AccessApprovalPolicy}.id`
|
||||||
|
)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
.where(buildFindFilter({ envId }, TableName.AccessApprovalPolicyEnvironment))
|
||||||
|
.whereNull(`${TableName.AccessApprovalPolicy}.deletedAt`)
|
||||||
|
.select(selectAllTableCols(TableName.AccessApprovalPolicyEnvironment));
|
||||||
|
return docs;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "findAvailablePoliciesByEnvId" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...accessApprovalPolicyEnvironmentOrm, findAvailablePoliciesByEnvId };
|
||||||
|
};
|
@@ -1,5 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
@@ -20,6 +21,7 @@ import {
|
|||||||
TAccessApprovalPolicyBypasserDALFactory
|
TAccessApprovalPolicyBypasserDALFactory
|
||||||
} from "./access-approval-policy-approver-dal";
|
} from "./access-approval-policy-approver-dal";
|
||||||
import { TAccessApprovalPolicyDALFactory } from "./access-approval-policy-dal";
|
import { TAccessApprovalPolicyDALFactory } from "./access-approval-policy-dal";
|
||||||
|
import { TAccessApprovalPolicyEnvironmentDALFactory } from "./access-approval-policy-environment-dal";
|
||||||
import {
|
import {
|
||||||
ApproverType,
|
ApproverType,
|
||||||
BypasserType,
|
BypasserType,
|
||||||
@@ -44,12 +46,14 @@ type TAccessApprovalPolicyServiceFactoryDep = {
|
|||||||
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
||||||
accessApprovalRequestReviewerDAL: Pick<TAccessApprovalRequestReviewerDALFactory, "update" | "delete">;
|
accessApprovalRequestReviewerDAL: Pick<TAccessApprovalRequestReviewerDALFactory, "update" | "delete">;
|
||||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "find">;
|
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "find">;
|
||||||
|
accessApprovalPolicyEnvironmentDAL: TAccessApprovalPolicyEnvironmentDALFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const accessApprovalPolicyServiceFactory = ({
|
export const accessApprovalPolicyServiceFactory = ({
|
||||||
accessApprovalPolicyDAL,
|
accessApprovalPolicyDAL,
|
||||||
accessApprovalPolicyApproverDAL,
|
accessApprovalPolicyApproverDAL,
|
||||||
accessApprovalPolicyBypasserDAL,
|
accessApprovalPolicyBypasserDAL,
|
||||||
|
accessApprovalPolicyEnvironmentDAL,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
@@ -62,21 +66,22 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
}: TAccessApprovalPolicyServiceFactoryDep): TAccessApprovalPolicyServiceFactory => {
|
}: TAccessApprovalPolicyServiceFactoryDep): TAccessApprovalPolicyServiceFactory => {
|
||||||
const $policyExists = async ({
|
const $policyExists = async ({
|
||||||
envId,
|
envId,
|
||||||
|
envIds,
|
||||||
secretPath,
|
secretPath,
|
||||||
policyId
|
policyId
|
||||||
}: {
|
}: {
|
||||||
envId: string;
|
envId?: string;
|
||||||
|
envIds?: string[];
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
policyId?: string;
|
policyId?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const policy = await accessApprovalPolicyDAL
|
if (!envId && !envIds) {
|
||||||
.findOne({
|
throw new BadRequestError({ message: "Must provide either envId or envIds" });
|
||||||
envId,
|
}
|
||||||
secretPath,
|
const policy = await accessApprovalPolicyDAL.findPolicyByEnvIdAndSecretPath({
|
||||||
deletedAt: null
|
secretPath,
|
||||||
})
|
envIds: envId ? [envId] : (envIds as string[])
|
||||||
.catch(() => null);
|
});
|
||||||
|
|
||||||
return policyId ? policy && policy.id !== policyId : Boolean(policy);
|
return policyId ? policy && policy.id !== policyId : Boolean(policy);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -92,6 +97,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
bypassers,
|
bypassers,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environment,
|
environment,
|
||||||
|
environments,
|
||||||
enforcementLevel,
|
enforcementLevel,
|
||||||
allowedSelfApprovals,
|
allowedSelfApprovals,
|
||||||
approvalsRequired
|
approvalsRequired
|
||||||
@@ -116,20 +122,31 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
ProjectPermissionSub.SecretApproval
|
ProjectPermissionSub.SecretApproval
|
||||||
);
|
);
|
||||||
const env = await projectEnvDAL.findOne({ slug: environment, projectId: project.id });
|
const mergedEnvs = (environment ? [environment] : environments) || [];
|
||||||
if (!env) throw new NotFoundError({ message: `Environment with slug '${environment}' not found` });
|
if (mergedEnvs.length === 0) {
|
||||||
|
throw new BadRequestError({ message: "Must provide either environment or environments" });
|
||||||
|
}
|
||||||
|
const envs = await projectEnvDAL.find({ $in: { slug: mergedEnvs }, projectId: project.id });
|
||||||
|
if (!envs.length || envs.length !== mergedEnvs.length) {
|
||||||
|
const notFoundEnvs = mergedEnvs.filter((env) => !envs.find((el) => el.slug === env));
|
||||||
|
throw new NotFoundError({ message: `One or more environments not found: ${notFoundEnvs.join(", ")}` });
|
||||||
|
}
|
||||||
|
|
||||||
if (await $policyExists({ envId: env.id, secretPath })) {
|
for (const env of envs) {
|
||||||
throw new BadRequestError({
|
// eslint-disable-next-line no-await-in-loop
|
||||||
message: `A policy for secret path '${secretPath}' already exists in environment '${environment}'`
|
if (await $policyExists({ envId: env.id, secretPath })) {
|
||||||
});
|
throw new BadRequestError({
|
||||||
|
message: `A policy for secret path '${secretPath}' already exists in environment '${env.slug}'`
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let approverUserIds = userApprovers;
|
let approverUserIds = userApprovers;
|
||||||
@@ -197,7 +214,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
const accessApproval = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
const accessApproval = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
||||||
const doc = await accessApprovalPolicyDAL.create(
|
const doc = await accessApprovalPolicyDAL.create(
|
||||||
{
|
{
|
||||||
envId: env.id,
|
envId: envs[0].id,
|
||||||
approvals,
|
approvals,
|
||||||
secretPath,
|
secretPath,
|
||||||
name,
|
name,
|
||||||
@@ -206,6 +223,10 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
await accessApprovalPolicyEnvironmentDAL.insertMany(
|
||||||
|
envs.map((el) => ({ policyId: doc.id, envId: el.id })),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
if (approverUserIds.length) {
|
if (approverUserIds.length) {
|
||||||
await accessApprovalPolicyApproverDAL.insertMany(
|
await accessApprovalPolicyApproverDAL.insertMany(
|
||||||
@@ -258,7 +279,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
return doc;
|
return doc;
|
||||||
});
|
});
|
||||||
|
|
||||||
return { ...accessApproval, environment: env, projectId: project.id };
|
return { ...accessApproval, environments: envs, projectId: project.id, environment: envs[0] };
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAccessApprovalPolicyByProjectSlug: TAccessApprovalPolicyServiceFactory["getAccessApprovalPolicyByProjectSlug"] =
|
const getAccessApprovalPolicyByProjectSlug: TAccessApprovalPolicyServiceFactory["getAccessApprovalPolicyByProjectSlug"] =
|
||||||
@@ -272,11 +293,15 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const accessApprovalPolicies = await accessApprovalPolicyDAL.find({ projectId: project.id, deletedAt: null });
|
const accessApprovalPolicies = await accessApprovalPolicyDAL.find({ projectId: project.id, deletedAt: null });
|
||||||
return accessApprovalPolicies;
|
return accessApprovalPolicies.map((policy) => ({
|
||||||
|
...policy,
|
||||||
|
environment: policy.environments[0]
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateAccessApprovalPolicy: TAccessApprovalPolicyServiceFactory["updateAccessApprovalPolicy"] = async ({
|
const updateAccessApprovalPolicy: TAccessApprovalPolicyServiceFactory["updateAccessApprovalPolicy"] = async ({
|
||||||
@@ -292,7 +317,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
approvals,
|
approvals,
|
||||||
enforcementLevel,
|
enforcementLevel,
|
||||||
allowedSelfApprovals,
|
allowedSelfApprovals,
|
||||||
approvalsRequired
|
approvalsRequired,
|
||||||
|
environments
|
||||||
}: TUpdateAccessApprovalPolicy) => {
|
}: TUpdateAccessApprovalPolicy) => {
|
||||||
const groupApprovers = approvers.filter((approver) => approver.type === ApproverType.Group);
|
const groupApprovers = approvers.filter((approver) => approver.type === ApproverType.Group);
|
||||||
|
|
||||||
@@ -320,16 +346,27 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let envs = accessApprovalPolicy.environments;
|
||||||
if (
|
if (
|
||||||
await $policyExists({
|
environments &&
|
||||||
envId: accessApprovalPolicy.envId,
|
(environments.length !== envs.length || environments.some((env) => !envs.find((el) => el.slug === env)))
|
||||||
secretPath: secretPath || accessApprovalPolicy.secretPath,
|
|
||||||
policyId: accessApprovalPolicy.id
|
|
||||||
})
|
|
||||||
) {
|
) {
|
||||||
throw new BadRequestError({
|
envs = await projectEnvDAL.find({ $in: { slug: environments }, projectId: accessApprovalPolicy.projectId });
|
||||||
message: `A policy for secret path '${secretPath}' already exists in environment '${accessApprovalPolicy.environment.slug}'`
|
}
|
||||||
});
|
|
||||||
|
for (const env of envs) {
|
||||||
|
if (
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await $policyExists({
|
||||||
|
envId: env.id,
|
||||||
|
secretPath: secretPath || accessApprovalPolicy.secretPath,
|
||||||
|
policyId: accessApprovalPolicy.id
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `A policy for secret path '${secretPath || accessApprovalPolicy.secretPath}' already exists in environment '${env.slug}'`
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
@@ -337,7 +374,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: accessApprovalPolicy.projectId,
|
projectId: accessApprovalPolicy.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
||||||
@@ -484,6 +522,14 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (environments) {
|
||||||
|
await accessApprovalPolicyEnvironmentDAL.delete({ policyId: doc.id }, tx);
|
||||||
|
await accessApprovalPolicyEnvironmentDAL.insertMany(
|
||||||
|
envs.map((env) => ({ policyId: doc.id, envId: env.id })),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
await accessApprovalPolicyBypasserDAL.delete({ policyId: doc.id }, tx);
|
await accessApprovalPolicyBypasserDAL.delete({ policyId: doc.id }, tx);
|
||||||
|
|
||||||
if (bypasserUserIds.length) {
|
if (bypasserUserIds.length) {
|
||||||
@@ -513,7 +559,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...updatedPolicy,
|
...updatedPolicy,
|
||||||
environment: accessApprovalPolicy.environment,
|
environments: accessApprovalPolicy.environments,
|
||||||
|
environment: accessApprovalPolicy.environments[0],
|
||||||
projectId: accessApprovalPolicy.projectId
|
projectId: accessApprovalPolicy.projectId
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -533,7 +580,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: policy.projectId,
|
projectId: policy.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Delete,
|
ProjectPermissionActions.Delete,
|
||||||
@@ -563,7 +611,10 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return policy;
|
return {
|
||||||
|
...policy,
|
||||||
|
environment: policy.environments[0]
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAccessPolicyCountByEnvSlug: TAccessApprovalPolicyServiceFactory["getAccessPolicyCountByEnvSlug"] = async ({
|
const getAccessPolicyCountByEnvSlug: TAccessApprovalPolicyServiceFactory["getAccessPolicyCountByEnvSlug"] = async ({
|
||||||
@@ -583,7 +634,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
if (!membership) {
|
if (!membership) {
|
||||||
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
||||||
@@ -592,11 +644,13 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
const environment = await projectEnvDAL.findOne({ projectId: project.id, slug: envSlug });
|
const environment = await projectEnvDAL.findOne({ projectId: project.id, slug: envSlug });
|
||||||
if (!environment) throw new NotFoundError({ message: `Environment with slug '${envSlug}' not found` });
|
if (!environment) throw new NotFoundError({ message: `Environment with slug '${envSlug}' not found` });
|
||||||
|
|
||||||
const policies = await accessApprovalPolicyDAL.find({
|
const policies = await accessApprovalPolicyDAL.find(
|
||||||
envId: environment.id,
|
{
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
deletedAt: null
|
deletedAt: null
|
||||||
});
|
},
|
||||||
|
{ envId: environment.id }
|
||||||
|
);
|
||||||
if (!policies) throw new NotFoundError({ message: `No policies found in environment with slug '${envSlug}'` });
|
if (!policies) throw new NotFoundError({ message: `No policies found in environment with slug '${envSlug}'` });
|
||||||
|
|
||||||
return { count: policies.length };
|
return { count: policies.length };
|
||||||
@@ -622,12 +676,16 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: policy.projectId,
|
projectId: policy.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
||||||
|
|
||||||
return policy;
|
return {
|
||||||
|
...policy,
|
||||||
|
environment: policy.environments[0]
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -26,7 +26,8 @@ export enum BypasserType {
|
|||||||
export type TCreateAccessApprovalPolicy = {
|
export type TCreateAccessApprovalPolicy = {
|
||||||
approvals: number;
|
approvals: number;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
environment: string;
|
environment?: string;
|
||||||
|
environments?: string[];
|
||||||
approvers: (
|
approvers: (
|
||||||
| { type: ApproverType.Group; id: string; sequence?: number }
|
| { type: ApproverType.Group; id: string; sequence?: number }
|
||||||
| { type: ApproverType.User; id?: string; username?: string; sequence?: number }
|
| { type: ApproverType.User; id?: string; username?: string; sequence?: number }
|
||||||
@@ -58,6 +59,7 @@ export type TUpdateAccessApprovalPolicy = {
|
|||||||
enforcementLevel?: EnforcementLevel;
|
enforcementLevel?: EnforcementLevel;
|
||||||
allowedSelfApprovals: boolean;
|
allowedSelfApprovals: boolean;
|
||||||
approvalsRequired?: { numberOfApprovals: number; stepNumber: number }[];
|
approvalsRequired?: { numberOfApprovals: number; stepNumber: number }[];
|
||||||
|
environments?: string[];
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TDeleteAccessApprovalPolicy = {
|
export type TDeleteAccessApprovalPolicy = {
|
||||||
@@ -113,6 +115,15 @@ export interface TAccessApprovalPolicyServiceFactory {
|
|||||||
slug: string;
|
slug: string;
|
||||||
position: number;
|
position: number;
|
||||||
};
|
};
|
||||||
|
environments: {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
projectId: string;
|
||||||
|
slug: string;
|
||||||
|
position: number;
|
||||||
|
}[];
|
||||||
projectId: string;
|
projectId: string;
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
@@ -153,6 +164,11 @@ export interface TAccessApprovalPolicyServiceFactory {
|
|||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
};
|
};
|
||||||
|
environments: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
}[];
|
||||||
projectId: string;
|
projectId: string;
|
||||||
}>;
|
}>;
|
||||||
updateAccessApprovalPolicy: ({
|
updateAccessApprovalPolicy: ({
|
||||||
@@ -168,13 +184,19 @@ export interface TAccessApprovalPolicyServiceFactory {
|
|||||||
approvals,
|
approvals,
|
||||||
enforcementLevel,
|
enforcementLevel,
|
||||||
allowedSelfApprovals,
|
allowedSelfApprovals,
|
||||||
approvalsRequired
|
approvalsRequired,
|
||||||
|
environments
|
||||||
}: TUpdateAccessApprovalPolicy) => Promise<{
|
}: TUpdateAccessApprovalPolicy) => Promise<{
|
||||||
environment: {
|
environment: {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
};
|
};
|
||||||
|
environments: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
}[];
|
||||||
projectId: string;
|
projectId: string;
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
@@ -225,6 +247,11 @@ export interface TAccessApprovalPolicyServiceFactory {
|
|||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
};
|
};
|
||||||
|
environments: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
}[];
|
||||||
projectId: string;
|
projectId: string;
|
||||||
bypassers: (
|
bypassers: (
|
||||||
| {
|
| {
|
||||||
@@ -276,6 +303,11 @@ export interface TAccessApprovalPolicyServiceFactory {
|
|||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
};
|
};
|
||||||
|
environments: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
}[];
|
||||||
projectId: string;
|
projectId: string;
|
||||||
bypassers: (
|
bypassers: (
|
||||||
| {
|
| {
|
||||||
|
@@ -65,7 +65,7 @@ export interface TAccessApprovalRequestDALFactory extends Omit<TOrmify<TableName
|
|||||||
deletedAt: Date | null | undefined;
|
deletedAt: Date | null | undefined;
|
||||||
};
|
};
|
||||||
projectId: string;
|
projectId: string;
|
||||||
environment: string;
|
environments: string[];
|
||||||
requestedByUser: {
|
requestedByUser: {
|
||||||
userId: string;
|
userId: string;
|
||||||
email: string | null | undefined;
|
email: string | null | undefined;
|
||||||
@@ -515,7 +515,17 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
|
|||||||
`accessApprovalReviewerUser.id`
|
`accessApprovalReviewerUser.id`
|
||||||
)
|
)
|
||||||
|
|
||||||
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
.leftJoin(
|
||||||
|
TableName.AccessApprovalPolicyEnvironment,
|
||||||
|
`${TableName.AccessApprovalPolicy}.id`,
|
||||||
|
`${TableName.AccessApprovalPolicyEnvironment}.policyId`
|
||||||
|
)
|
||||||
|
|
||||||
|
.leftJoin(
|
||||||
|
TableName.Environment,
|
||||||
|
`${TableName.AccessApprovalPolicyEnvironment}.envId`,
|
||||||
|
`${TableName.Environment}.id`
|
||||||
|
)
|
||||||
.select(selectAllTableCols(TableName.AccessApprovalRequest))
|
.select(selectAllTableCols(TableName.AccessApprovalRequest))
|
||||||
.select(
|
.select(
|
||||||
tx.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover),
|
tx.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover),
|
||||||
@@ -683,6 +693,11 @@ export const accessApprovalRequestDALFactory = (db: TDbClient): TAccessApprovalR
|
|||||||
lastName,
|
lastName,
|
||||||
username
|
username
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "environment",
|
||||||
|
label: "environments" as const,
|
||||||
|
mapper: ({ environment }) => environment
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import msFn from "ms";
|
import msFn from "ms";
|
||||||
|
|
||||||
import { ProjectMembershipRole } from "@app/db/schemas";
|
import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { groupBy } from "@app/lib/fn";
|
import { groupBy } from "@app/lib/fn";
|
||||||
@@ -86,6 +86,25 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
projectMicrosoftTeamsConfigDAL,
|
projectMicrosoftTeamsConfigDAL,
|
||||||
projectSlackConfigDAL
|
projectSlackConfigDAL
|
||||||
}: TSecretApprovalRequestServiceFactoryDep): TAccessApprovalRequestServiceFactory => {
|
}: TSecretApprovalRequestServiceFactoryDep): TAccessApprovalRequestServiceFactory => {
|
||||||
|
const $getEnvironmentFromPermissions = (permissions: unknown): string | null => {
|
||||||
|
if (!Array.isArray(permissions) || permissions.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstPermission = permissions[0] as unknown[];
|
||||||
|
if (!Array.isArray(firstPermission) || firstPermission.length < 3) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata = firstPermission[2] as Record<string, unknown>;
|
||||||
|
if (typeof metadata === "object" && metadata !== null && "environment" in metadata) {
|
||||||
|
const env = metadata.environment;
|
||||||
|
return typeof env === "string" ? env : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const createAccessApprovalRequest: TAccessApprovalRequestServiceFactory["createAccessApprovalRequest"] = async ({
|
const createAccessApprovalRequest: TAccessApprovalRequestServiceFactory["createAccessApprovalRequest"] = async ({
|
||||||
isTemporary,
|
isTemporary,
|
||||||
temporaryRange,
|
temporaryRange,
|
||||||
@@ -107,7 +126,8 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
if (!membership) {
|
if (!membership) {
|
||||||
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
||||||
@@ -216,7 +236,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const requesterFullName = `${requestedByUser.firstName} ${requestedByUser.lastName}`;
|
const requesterFullName = `${requestedByUser.firstName} ${requestedByUser.lastName}`;
|
||||||
const approvalUrl = `${cfg.SITE_URL}/projects/${project.id}/secret-manager/approval`;
|
const approvalUrl = `${cfg.SITE_URL}/projects/secret-management/${project.id}/approval`;
|
||||||
|
|
||||||
await triggerWorkflowIntegrationNotification({
|
await triggerWorkflowIntegrationNotification({
|
||||||
input: {
|
input: {
|
||||||
@@ -289,7 +309,8 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
if (!membership) {
|
if (!membership) {
|
||||||
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
||||||
@@ -306,6 +327,15 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
requests = requests.filter((request) => request.environment === envSlug);
|
requests = requests.filter((request) => request.environment === envSlug);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requests = requests.map((request) => {
|
||||||
|
const permissionEnvironment = $getEnvironmentFromPermissions(request.permissions);
|
||||||
|
|
||||||
|
if (permissionEnvironment) {
|
||||||
|
request.environmentName = permissionEnvironment;
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
});
|
||||||
|
|
||||||
return { requests };
|
return { requests };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -323,19 +353,34 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: `Secret approval request with ID '${requestId}' not found` });
|
throw new NotFoundError({ message: `Secret approval request with ID '${requestId}' not found` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { policy, environment } = accessApprovalRequest;
|
const { policy, environments, permissions } = accessApprovalRequest;
|
||||||
if (policy.deletedAt) {
|
if (policy.deletedAt) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "The policy associated with this access request has been deleted."
|
message: "The policy associated with this access request has been deleted."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const permissionEnvironment = $getEnvironmentFromPermissions(permissions);
|
||||||
|
if (
|
||||||
|
!permissionEnvironment ||
|
||||||
|
(!environments.includes(permissionEnvironment) && status === ApprovalStatus.APPROVED)
|
||||||
|
) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `The original policy ${policy.name} is not attached to environment '${permissionEnvironment}'.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const environment = await projectEnvDAL.findOne({
|
||||||
|
projectId: accessApprovalRequest.projectId,
|
||||||
|
slug: permissionEnvironment
|
||||||
|
});
|
||||||
|
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission({
|
const { membership, hasRole } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId: accessApprovalRequest.projectId,
|
projectId: accessApprovalRequest.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!membership) {
|
if (!membership) {
|
||||||
@@ -550,8 +595,8 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
requesterEmail: actingUser.email,
|
requesterEmail: actingUser.email,
|
||||||
bypassReason: bypassReason || "No reason provided",
|
bypassReason: bypassReason || "No reason provided",
|
||||||
secretPath: policy.secretPath || "/",
|
secretPath: policy.secretPath || "/",
|
||||||
environment,
|
environment: environment?.name || permissionEnvironment,
|
||||||
approvalUrl: `${cfg.SITE_URL}/projects/${project.id}/secret-manager/approval`,
|
approvalUrl: `${cfg.SITE_URL}/projects/secret-management/${project.id}/approval`,
|
||||||
requestType: "access"
|
requestType: "access"
|
||||||
},
|
},
|
||||||
template: SmtpTemplates.AccessSecretRequestBypassed
|
template: SmtpTemplates.AccessSecretRequestBypassed
|
||||||
@@ -582,7 +627,8 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
if (!membership) {
|
if (!membership) {
|
||||||
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
@@ -37,7 +38,8 @@ export const assumePrivilegeServiceFactory = ({
|
|||||||
actorId: actorPermissionDetails.id,
|
actorId: actorPermissionDetails.id,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod: actorPermissionDetails.authMethod,
|
actorAuthMethod: actorPermissionDetails.authMethod,
|
||||||
actorOrgId: actorPermissionDetails.orgId
|
actorOrgId: actorPermissionDetails.orgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
|
|
||||||
if (targetActorType === ActorType.USER) {
|
if (targetActorType === ActorType.USER) {
|
||||||
@@ -58,7 +60,8 @@ export const assumePrivilegeServiceFactory = ({
|
|||||||
actorId: targetActorId,
|
actorId: targetActorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod: actorPermissionDetails.authMethod,
|
actorAuthMethod: actorPermissionDetails.authMethod,
|
||||||
actorOrgId: actorPermissionDetails.orgId
|
actorOrgId: actorPermissionDetails.orgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { requestContext } from "@fastify/request-context";
|
import { requestContext } from "@fastify/request-context";
|
||||||
|
|
||||||
|
import { ActionProjectType } 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 { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
@@ -37,7 +38,8 @@ export const auditLogServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: filter.projectId,
|
projectId: filter.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -468,7 +468,11 @@ export enum EventType {
|
|||||||
|
|
||||||
CREATE_PROJECT = "create-project",
|
CREATE_PROJECT = "create-project",
|
||||||
UPDATE_PROJECT = "update-project",
|
UPDATE_PROJECT = "update-project",
|
||||||
DELETE_PROJECT = "delete-project"
|
DELETE_PROJECT = "delete-project",
|
||||||
|
|
||||||
|
CREATE_SECRET_REMINDER = "create-secret-reminder",
|
||||||
|
GET_SECRET_REMINDER = "get-secret-reminder",
|
||||||
|
DELETE_SECRET_REMINDER = "delete-secret-reminder"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const filterableSecretEvents: EventType[] = [
|
export const filterableSecretEvents: EventType[] = [
|
||||||
@@ -3326,6 +3330,31 @@ interface SecretScanningConfigUpdateEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SecretReminderCreateEvent {
|
||||||
|
type: EventType.CREATE_SECRET_REMINDER;
|
||||||
|
metadata: {
|
||||||
|
secretId: string;
|
||||||
|
message?: string | null;
|
||||||
|
repeatDays?: number | null;
|
||||||
|
nextReminderDate?: string | null;
|
||||||
|
recipients?: string[] | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SecretReminderGetEvent {
|
||||||
|
type: EventType.GET_SECRET_REMINDER;
|
||||||
|
metadata: {
|
||||||
|
secretId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SecretReminderDeleteEvent {
|
||||||
|
type: EventType.DELETE_SECRET_REMINDER;
|
||||||
|
metadata: {
|
||||||
|
secretId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface SecretScanningConfigReadEvent {
|
interface SecretScanningConfigReadEvent {
|
||||||
type: EventType.SECRET_SCANNING_CONFIG_GET;
|
type: EventType.SECRET_SCANNING_CONFIG_GET;
|
||||||
metadata?: Record<string, never>; // not needed, based off projectId
|
metadata?: Record<string, never>; // not needed, based off projectId
|
||||||
@@ -3689,4 +3718,7 @@ export type Event =
|
|||||||
| OrgUpdateEvent
|
| OrgUpdateEvent
|
||||||
| ProjectCreateEvent
|
| ProjectCreateEvent
|
||||||
| ProjectUpdateEvent
|
| ProjectUpdateEvent
|
||||||
| ProjectDeleteEvent;
|
| ProjectDeleteEvent
|
||||||
|
| SecretReminderCreateEvent
|
||||||
|
| SecretReminderGetEvent
|
||||||
|
| SecretReminderDeleteEvent;
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal";
|
import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
@@ -77,7 +78,8 @@ export const certificateAuthorityCrlServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
import RE2 from "re2";
|
import RE2 from "re2";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import {
|
import {
|
||||||
@@ -84,7 +85,8 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(actorOrgId);
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
@@ -200,7 +202,8 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
|
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
@@ -297,7 +300,8 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
|
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
@@ -385,7 +389,8 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
|
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
|
||||||
@@ -432,7 +437,8 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
|
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import {
|
import {
|
||||||
@@ -78,7 +79,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -207,7 +209,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(actorOrgId);
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
@@ -358,7 +361,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
|
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
|
||||||
@@ -423,7 +427,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
|
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
|
||||||
@@ -487,7 +492,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
// verify user has access to each env in request
|
// verify user has access to each env in request
|
||||||
@@ -530,7 +536,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionDynamicSecretActions.ReadRootCredential,
|
ProjectPermissionDynamicSecretActions.ReadRootCredential,
|
||||||
@@ -578,7 +585,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
|
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
|
||||||
@@ -615,7 +623,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const userAccessibleFolderMappings = folderMappings.filter(({ path, environment }) =>
|
const userAccessibleFolderMappings = folderMappings.filter(({ path, environment }) =>
|
||||||
@@ -659,7 +668,8 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const folders = await folderDAL.findBySecretPathMultiEnv(projectId, environmentSlugs, path);
|
const folders = await folderDAL.findBySecretPathMultiEnv(projectId, environmentSlugs, path);
|
||||||
|
@@ -566,6 +566,14 @@ export const gatewayServiceFactory = ({
|
|||||||
if (!gateway) throw new NotFoundError({ message: `Gateway with ID ${gatewayId} not found.` });
|
if (!gateway) throw new NotFoundError({ message: `Gateway with ID ${gatewayId} not found.` });
|
||||||
|
|
||||||
const orgGatewayConfig = await orgGatewayConfigDAL.findById(gateway.orgGatewayRootCaId);
|
const orgGatewayConfig = await orgGatewayConfigDAL.findById(gateway.orgGatewayRootCaId);
|
||||||
|
|
||||||
|
const orgLicensePlan = await licenseService.getPlan(orgGatewayConfig.orgId);
|
||||||
|
if (!orgLicensePlan.gateway) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Please upgrade your instance to Infisical's Enterprise plan to use gateways."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const { decryptor: orgKmsDecryptor } = await kmsService.createCipherPairWithDataKey({
|
const { decryptor: orgKmsDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
type: KmsDataKey.Organization,
|
type: KmsDataKey.Organization,
|
||||||
orgId: orgGatewayConfig.orgId
|
orgId: orgGatewayConfig.orgId
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
import { packRules } from "@casl/ability/extra";
|
import { packRules } from "@casl/ability/extra";
|
||||||
|
|
||||||
import { TableName } from "@app/db/schemas";
|
import { ActionProjectType, TableName } from "@app/db/schemas";
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
||||||
import { ms } from "@app/lib/ms";
|
import { ms } from "@app/lib/ms";
|
||||||
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||||
@@ -61,7 +61,8 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionIdentityActions.Edit,
|
ProjectPermissionIdentityActions.Edit,
|
||||||
@@ -72,7 +73,8 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actorId: identityId,
|
actorId: identityId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
|
|
||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
// we need to validate that the privilege given is not higher than the assigning users permission
|
||||||
@@ -158,7 +160,8 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionIdentityActions.Edit,
|
ProjectPermissionIdentityActions.Edit,
|
||||||
@@ -169,7 +172,8 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actorId: identityProjectMembership.identityId,
|
actorId: identityProjectMembership.identityId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
|
|
||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
// we need to validate that the privilege given is not higher than the assigning users permission
|
||||||
@@ -256,7 +260,8 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionIdentityActions.Edit,
|
ProjectPermissionIdentityActions.Edit,
|
||||||
@@ -267,7 +272,8 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actorId: identityProjectMembership.identityId,
|
actorId: identityProjectMembership.identityId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
@@ -315,7 +321,8 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionIdentityActions.Read,
|
ProjectPermissionIdentityActions.Read,
|
||||||
@@ -349,7 +356,8 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionIdentityActions.Read,
|
ProjectPermissionIdentityActions.Read,
|
||||||
@@ -384,7 +392,8 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionIdentityActions.Read,
|
ProjectPermissionIdentityActions.Read,
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { ForbiddenError, MongoAbility, RawRuleOf, subject } from "@casl/ability";
|
import { ForbiddenError, MongoAbility, RawRuleOf, subject } from "@casl/ability";
|
||||||
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
||||||
import { ms } from "@app/lib/ms";
|
import { ms } from "@app/lib/ms";
|
||||||
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||||
@@ -72,7 +73,8 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -85,7 +87,8 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId: identityId,
|
actorId: identityId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
|
|
||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
// we need to validate that the privilege given is not higher than the assigning users permission
|
||||||
@@ -172,7 +175,8 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -185,7 +189,8 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId: identityProjectMembership.identityId,
|
actorId: identityProjectMembership.identityId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
|
|
||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
// we need to validate that the privilege given is not higher than the assigning users permission
|
||||||
@@ -288,7 +293,8 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionIdentityActions.Edit,
|
ProjectPermissionIdentityActions.Edit,
|
||||||
@@ -300,7 +306,8 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId: identityProjectMembership.identityId,
|
actorId: identityProjectMembership.identityId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
const permissionBoundary = validatePrivilegeChangeOperation(
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
membership.shouldUseNewPrivilegeSystem,
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
@@ -359,7 +366,8 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionIdentityActions.Read,
|
ProjectPermissionIdentityActions.Read,
|
||||||
@@ -401,7 +409,8 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
||||||
import { isValidIp } from "@app/lib/ip";
|
import { isValidIp } from "@app/lib/ip";
|
||||||
@@ -78,7 +79,8 @@ export const kmipServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.KMS
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -131,7 +133,8 @@ export const kmipServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: kmipClient.projectId,
|
projectId: kmipClient.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.KMS
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -162,7 +165,8 @@ export const kmipServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: kmipClient.projectId,
|
projectId: kmipClient.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.KMS
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -195,7 +199,8 @@ export const kmipServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: kmipClient.projectId,
|
projectId: kmipClient.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.KMS
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionKmipActions.ReadClients, ProjectPermissionSub.Kmip);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionKmipActions.ReadClients, ProjectPermissionSub.Kmip);
|
||||||
@@ -216,7 +221,8 @@ export const kmipServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.KMS
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionKmipActions.ReadClients, ProjectPermissionSub.Kmip);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionKmipActions.ReadClients, ProjectPermissionSub.Kmip);
|
||||||
@@ -252,7 +258,8 @@ export const kmipServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: kmipClient.projectId,
|
projectId: kmipClient.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.KMS
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { MongoAbility, RawRuleOf } from "@casl/ability";
|
import { MongoAbility, RawRuleOf } from "@casl/ability";
|
||||||
import { MongoQuery } from "@ucast/mongo2js";
|
import { MongoQuery } from "@ucast/mongo2js";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
import { OrgPermissionSet } from "./org-permission";
|
import { OrgPermissionSet } from "./org-permission";
|
||||||
@@ -20,6 +21,7 @@ export type TGetUserProjectPermissionArg = {
|
|||||||
userId: string;
|
userId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
authMethod: ActorAuthMethod;
|
authMethod: ActorAuthMethod;
|
||||||
|
actionProjectType: ActionProjectType;
|
||||||
userOrgId?: string;
|
userOrgId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,12 +29,14 @@ export type TGetIdentityProjectPermissionArg = {
|
|||||||
identityId: string;
|
identityId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
identityOrgId?: string;
|
identityOrgId?: string;
|
||||||
|
actionProjectType: ActionProjectType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGetServiceTokenProjectPermissionArg = {
|
export type TGetServiceTokenProjectPermissionArg = {
|
||||||
serviceTokenId: string;
|
serviceTokenId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
actorOrgId?: string;
|
actorOrgId?: string;
|
||||||
|
actionProjectType: ActionProjectType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGetProjectPermissionArg = {
|
export type TGetProjectPermissionArg = {
|
||||||
@@ -41,6 +45,7 @@ export type TGetProjectPermissionArg = {
|
|||||||
projectId: string;
|
projectId: string;
|
||||||
actorAuthMethod: ActorAuthMethod;
|
actorAuthMethod: ActorAuthMethod;
|
||||||
actorOrgId?: string;
|
actorOrgId?: string;
|
||||||
|
actionProjectType: ActionProjectType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TPermissionServiceFactory = {
|
export type TPermissionServiceFactory = {
|
||||||
@@ -138,7 +143,13 @@ export type TPermissionServiceFactory = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
getUserProjectPermission: ({ userId, projectId, authMethod, userOrgId }: TGetUserProjectPermissionArg) => Promise<{
|
getUserProjectPermission: ({
|
||||||
|
userId,
|
||||||
|
projectId,
|
||||||
|
authMethod,
|
||||||
|
userOrgId,
|
||||||
|
actionProjectType
|
||||||
|
}: TGetUserProjectPermissionArg) => Promise<{
|
||||||
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
||||||
membership: {
|
membership: {
|
||||||
id: string;
|
id: string;
|
||||||
|
@@ -5,6 +5,7 @@ import { MongoQuery } from "@ucast/mongo2js";
|
|||||||
import handlebars from "handlebars";
|
import handlebars from "handlebars";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ActionProjectType,
|
||||||
OrgMembershipRole,
|
OrgMembershipRole,
|
||||||
ProjectMembershipRole,
|
ProjectMembershipRole,
|
||||||
ServiceTokenScopes,
|
ServiceTokenScopes,
|
||||||
@@ -213,7 +214,8 @@ export const permissionServiceFactory = ({
|
|||||||
userId,
|
userId,
|
||||||
projectId,
|
projectId,
|
||||||
authMethod,
|
authMethod,
|
||||||
userOrgId
|
userOrgId,
|
||||||
|
actionProjectType
|
||||||
}: TGetUserProjectPermissionArg): Promise<TProjectPermissionRT<ActorType.USER>> => {
|
}: TGetUserProjectPermissionArg): Promise<TProjectPermissionRT<ActorType.USER>> => {
|
||||||
const userProjectPermission = await permissionDAL.getProjectPermission(userId, projectId);
|
const userProjectPermission = await permissionDAL.getProjectPermission(userId, projectId);
|
||||||
if (!userProjectPermission) throw new ForbiddenRequestError({ name: "User not a part of the specified project" });
|
if (!userProjectPermission) throw new ForbiddenRequestError({ name: "User not a part of the specified project" });
|
||||||
@@ -240,6 +242,12 @@ export const permissionServiceFactory = ({
|
|||||||
userProjectPermission.orgRole
|
userProjectPermission.orgRole
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (actionProjectType !== ActionProjectType.Any && actionProjectType !== userProjectPermission.projectType) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `The project is of type ${userProjectPermission.projectType}. Operations of type ${actionProjectType} are not allowed.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// join two permissions and pass to build the final permission set
|
// join two permissions and pass to build the final permission set
|
||||||
const rolePermissions = userProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || [];
|
const rolePermissions = userProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || [];
|
||||||
const additionalPrivileges =
|
const additionalPrivileges =
|
||||||
@@ -287,7 +295,8 @@ export const permissionServiceFactory = ({
|
|||||||
const getIdentityProjectPermission = async ({
|
const getIdentityProjectPermission = async ({
|
||||||
identityId,
|
identityId,
|
||||||
projectId,
|
projectId,
|
||||||
identityOrgId
|
identityOrgId,
|
||||||
|
actionProjectType
|
||||||
}: TGetIdentityProjectPermissionArg): Promise<TProjectPermissionRT<ActorType.IDENTITY>> => {
|
}: TGetIdentityProjectPermissionArg): Promise<TProjectPermissionRT<ActorType.IDENTITY>> => {
|
||||||
const identityProjectPermission = await permissionDAL.getProjectIdentityPermission(identityId, projectId);
|
const identityProjectPermission = await permissionDAL.getProjectIdentityPermission(identityId, projectId);
|
||||||
if (!identityProjectPermission)
|
if (!identityProjectPermission)
|
||||||
@@ -307,6 +316,12 @@ export const permissionServiceFactory = ({
|
|||||||
throw new ForbiddenRequestError({ name: "Identity is not a member of the specified organization" });
|
throw new ForbiddenRequestError({ name: "Identity is not a member of the specified organization" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (actionProjectType !== ActionProjectType.Any && actionProjectType !== identityProjectPermission.projectType) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `The project is of type ${identityProjectPermission.projectType}. Operations of type ${actionProjectType} are not allowed.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const rolePermissions =
|
const rolePermissions =
|
||||||
identityProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || [];
|
identityProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || [];
|
||||||
const additionalPrivileges =
|
const additionalPrivileges =
|
||||||
@@ -361,7 +376,8 @@ export const permissionServiceFactory = ({
|
|||||||
const getServiceTokenProjectPermission = async ({
|
const getServiceTokenProjectPermission = async ({
|
||||||
serviceTokenId,
|
serviceTokenId,
|
||||||
projectId,
|
projectId,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType
|
||||||
}: TGetServiceTokenProjectPermissionArg) => {
|
}: TGetServiceTokenProjectPermissionArg) => {
|
||||||
const serviceToken = await serviceTokenDAL.findById(serviceTokenId);
|
const serviceToken = await serviceTokenDAL.findById(serviceTokenId);
|
||||||
if (!serviceToken) throw new NotFoundError({ message: `Service token with ID '${serviceTokenId}' not found` });
|
if (!serviceToken) throw new NotFoundError({ message: `Service token with ID '${serviceTokenId}' not found` });
|
||||||
@@ -386,6 +402,12 @@ export const permissionServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (actionProjectType !== ActionProjectType.Any && actionProjectType !== serviceTokenProject.type) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `The project is of type ${serviceTokenProject.type}. Operations of type ${actionProjectType} are not allowed.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const scopes = ServiceTokenScopes.parse(serviceToken.scopes || []);
|
const scopes = ServiceTokenScopes.parse(serviceToken.scopes || []);
|
||||||
return {
|
return {
|
||||||
permission: buildServiceTokenProjectPermission(scopes, serviceToken.permissions),
|
permission: buildServiceTokenProjectPermission(scopes, serviceToken.permissions),
|
||||||
@@ -537,7 +559,8 @@ export const permissionServiceFactory = ({
|
|||||||
actorId: inputActorId,
|
actorId: inputActorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType
|
||||||
}: TGetProjectPermissionArg): Promise<TProjectPermissionRT<T>> => {
|
}: TGetProjectPermissionArg): Promise<TProjectPermissionRT<T>> => {
|
||||||
let actor = inputActor;
|
let actor = inputActor;
|
||||||
let actorId = inputActorId;
|
let actorId = inputActorId;
|
||||||
@@ -558,19 +581,22 @@ export const permissionServiceFactory = ({
|
|||||||
userId: actorId,
|
userId: actorId,
|
||||||
projectId,
|
projectId,
|
||||||
authMethod: actorAuthMethod,
|
authMethod: actorAuthMethod,
|
||||||
userOrgId: actorOrgId
|
userOrgId: actorOrgId,
|
||||||
|
actionProjectType
|
||||||
}) as Promise<TProjectPermissionRT<T>>;
|
}) as Promise<TProjectPermissionRT<T>>;
|
||||||
case ActorType.SERVICE:
|
case ActorType.SERVICE:
|
||||||
return getServiceTokenProjectPermission({
|
return getServiceTokenProjectPermission({
|
||||||
serviceTokenId: actorId,
|
serviceTokenId: actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType
|
||||||
}) as Promise<TProjectPermissionRT<T>>;
|
}) as Promise<TProjectPermissionRT<T>>;
|
||||||
case ActorType.IDENTITY:
|
case ActorType.IDENTITY:
|
||||||
return getIdentityProjectPermission({
|
return getIdentityProjectPermission({
|
||||||
identityId: actorId,
|
identityId: actorId,
|
||||||
projectId,
|
projectId,
|
||||||
identityOrgId: actorOrgId
|
identityOrgId: actorOrgId,
|
||||||
|
actionProjectType
|
||||||
}) as Promise<TProjectPermissionRT<T>>;
|
}) as Promise<TProjectPermissionRT<T>>;
|
||||||
default:
|
default:
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { Event, EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { Event, EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { ProjectPermissionCommitsActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionCommitsActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
@@ -348,7 +349,8 @@ export const pitServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(userPermission).throwUnlessCan(
|
ForbiddenError.from(userPermission).throwUnlessCan(
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { ProjectType } from "@app/db/schemas";
|
||||||
import {
|
import {
|
||||||
InfisicalProjectTemplate,
|
InfisicalProjectTemplate,
|
||||||
TUnpackedPermission
|
TUnpackedPermission
|
||||||
@@ -6,18 +7,21 @@ import { getPredefinedRoles } from "@app/services/project-role/project-role-fns"
|
|||||||
|
|
||||||
import { ProjectTemplateDefaultEnvironments } from "./project-template-constants";
|
import { ProjectTemplateDefaultEnvironments } from "./project-template-constants";
|
||||||
|
|
||||||
export const getDefaultProjectTemplate = (orgId: string) => ({
|
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({ projectId: "project-template" }) as Array<{
|
roles: [...getPredefinedRoles({ projectId: "project-template", projectType: type })].map(
|
||||||
name: string;
|
({ name, slug, permissions }) => ({
|
||||||
slug: string;
|
name,
|
||||||
permissions: TUnpackedPermission[];
|
slug,
|
||||||
}>,
|
permissions: permissions as TUnpackedPermission[]
|
||||||
|
})
|
||||||
|
),
|
||||||
orgId
|
orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
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-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
@@ -29,11 +29,13 @@ const $unpackProjectTemplate = ({ roles, environments, ...rest }: TProjectTempla
|
|||||||
...rest,
|
...rest,
|
||||||
environments: environments as TProjectTemplateEnvironment[],
|
environments: environments as TProjectTemplateEnvironment[],
|
||||||
roles: [
|
roles: [
|
||||||
...getPredefinedRoles({ projectId: "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)
|
||||||
@@ -46,7 +48,10 @@ export const projectTemplateServiceFactory = ({
|
|||||||
permissionService,
|
permissionService,
|
||||||
projectTemplateDAL
|
projectTemplateDAL
|
||||||
}: TProjectTemplatesServiceFactoryDep): TProjectTemplateServiceFactory => {
|
}: TProjectTemplatesServiceFactoryDep): TProjectTemplateServiceFactory => {
|
||||||
const listProjectTemplatesByOrg: TProjectTemplateServiceFactory["listProjectTemplatesByOrg"] = async (actor) => {
|
const listProjectTemplatesByOrg: TProjectTemplateServiceFactory["listProjectTemplatesByOrg"] = async (
|
||||||
|
actor,
|
||||||
|
type
|
||||||
|
) => {
|
||||||
const plan = await licenseService.getPlan(actor.orgId);
|
const plan = await licenseService.getPlan(actor.orgId);
|
||||||
|
|
||||||
if (!plan.projectTemplates)
|
if (!plan.projectTemplates)
|
||||||
@@ -65,11 +70,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 +142,7 @@ export const projectTemplateServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createProjectTemplate: TProjectTemplateServiceFactory["createProjectTemplate"] = async (
|
const createProjectTemplate: TProjectTemplateServiceFactory["createProjectTemplate"] = async (
|
||||||
{ roles, environments, ...params },
|
{ roles, environments, type, ...params },
|
||||||
actor
|
actor
|
||||||
) => {
|
) => {
|
||||||
const plan = await licenseService.getPlan(actor.orgId);
|
const plan = await licenseService.getPlan(actor.orgId);
|
||||||
@@ -154,6 +162,10 @@ 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) {
|
if (environments && plan.environmentLimit !== null && environments.length > plan.environmentLimit) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
@@ -176,8 +188,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: environments ? JSON.stringify(environments ?? ProjectTemplateDefaultEnvironments) : null,
|
environments:
|
||||||
orgId: actor.orgId
|
type === ProjectType.SecretManager ? JSON.stringify(environments ?? ProjectTemplateDefaultEnvironments) : null,
|
||||||
|
orgId: actor.orgId,
|
||||||
|
type
|
||||||
});
|
});
|
||||||
|
|
||||||
return $unpackProjectTemplate(projectTemplate);
|
return $unpackProjectTemplate(projectTemplate);
|
||||||
@@ -208,6 +222,11 @@ 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) {
|
if (environments && plan.environmentLimit !== null && environments.length > plan.environmentLimit) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ProjectMembershipRole, TProjectEnvironments } from "@app/db/schemas";
|
import { ProjectMembershipRole, 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 { OrgServiceActor } from "@app/lib/types";
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
|
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
|
||||||
@@ -15,6 +15,7 @@ export type TProjectTemplateRole = {
|
|||||||
|
|
||||||
export type TCreateProjectTemplateDTO = {
|
export type TCreateProjectTemplateDTO = {
|
||||||
name: string;
|
name: string;
|
||||||
|
type: ProjectType;
|
||||||
description?: string;
|
description?: string;
|
||||||
roles: TProjectTemplateRole[];
|
roles: TProjectTemplateRole[];
|
||||||
environments?: TProjectTemplateEnvironment[] | null;
|
environments?: TProjectTemplateEnvironment[] | null;
|
||||||
@@ -29,11 +30,15 @@ export enum InfisicalProjectTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type TProjectTemplateServiceFactory = {
|
export type TProjectTemplateServiceFactory = {
|
||||||
listProjectTemplatesByOrg: (actor: OrgServiceActor) => Promise<
|
listProjectTemplatesByOrg: (
|
||||||
|
actor: OrgServiceActor,
|
||||||
|
type?: ProjectType
|
||||||
|
) => Promise<
|
||||||
(
|
(
|
||||||
| {
|
| {
|
||||||
id: string;
|
id: string;
|
||||||
name: InfisicalProjectTemplate;
|
name: InfisicalProjectTemplate;
|
||||||
|
type: string;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
description: string;
|
description: string;
|
||||||
@@ -58,6 +63,7 @@ export type TProjectTemplateServiceFactory = {
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
environments: TProjectTemplateEnvironment[];
|
environments: TProjectTemplateEnvironment[];
|
||||||
|
type: string;
|
||||||
roles: {
|
roles: {
|
||||||
permissions: {
|
permissions: {
|
||||||
action: string[];
|
action: string[];
|
||||||
@@ -94,6 +100,7 @@ export type TProjectTemplateServiceFactory = {
|
|||||||
}[];
|
}[];
|
||||||
name: string;
|
name: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
|
type: string;
|
||||||
id: string;
|
id: string;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
@@ -118,6 +125,7 @@ export type TProjectTemplateServiceFactory = {
|
|||||||
name: string;
|
name: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
type: string;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
description?: string | null | undefined;
|
description?: string | null | undefined;
|
||||||
@@ -140,6 +148,7 @@ export type TProjectTemplateServiceFactory = {
|
|||||||
name: string;
|
name: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
type: string;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
description?: string | null | undefined;
|
description?: string | null | undefined;
|
||||||
@@ -162,6 +171,7 @@ export type TProjectTemplateServiceFactory = {
|
|||||||
}[];
|
}[];
|
||||||
name: string;
|
name: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
|
type: string;
|
||||||
id: string;
|
id: string;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
@@ -184,6 +194,7 @@ export type TProjectTemplateServiceFactory = {
|
|||||||
name: string;
|
name: string;
|
||||||
}[];
|
}[];
|
||||||
name: string;
|
name: string;
|
||||||
|
type: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
id: string;
|
id: string;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
|
import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
|
||||||
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
||||||
|
|
||||||
import { TableName } from "@app/db/schemas";
|
import { ActionProjectType, TableName } from "@app/db/schemas";
|
||||||
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
||||||
import { ms } from "@app/lib/ms";
|
import { ms } from "@app/lib/ms";
|
||||||
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||||
@@ -61,7 +61,8 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: projectMembership.projectId,
|
projectId: projectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member);
|
||||||
const { permission: targetUserPermission, membership } = await permissionService.getProjectPermission({
|
const { permission: targetUserPermission, membership } = await permissionService.getProjectPermission({
|
||||||
@@ -69,7 +70,8 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId: projectMembership.userId,
|
actorId: projectMembership.userId,
|
||||||
projectId: projectMembership.projectId,
|
projectId: projectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
|
|
||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
// we need to validate that the privilege given is not higher than the assigning users permission
|
||||||
@@ -164,7 +166,8 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: projectMembership.projectId,
|
projectId: projectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member);
|
||||||
const { permission: targetUserPermission } = await permissionService.getProjectPermission({
|
const { permission: targetUserPermission } = await permissionService.getProjectPermission({
|
||||||
@@ -172,7 +175,8 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId: projectMembership.userId,
|
actorId: projectMembership.userId,
|
||||||
projectId: projectMembership.projectId,
|
projectId: projectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
|
|
||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
// we need to validate that the privilege given is not higher than the assigning users permission
|
||||||
@@ -272,7 +276,8 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: projectMembership.projectId,
|
projectId: projectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member);
|
||||||
|
|
||||||
@@ -317,7 +322,8 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: projectMembership.projectId,
|
projectId: projectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member);
|
||||||
|
|
||||||
@@ -343,7 +349,8 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: projectMembership.projectId,
|
projectId: projectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member);
|
||||||
|
|
||||||
|
@@ -23,6 +23,7 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
filter: TFindFilter<TSecretApprovalPolicies & { projectId: string }>,
|
filter: TFindFilter<TSecretApprovalPolicies & { projectId: string }>,
|
||||||
customFilter?: {
|
customFilter?: {
|
||||||
sapId?: string;
|
sapId?: string;
|
||||||
|
envId?: string;
|
||||||
}
|
}
|
||||||
) =>
|
) =>
|
||||||
tx(TableName.SecretApprovalPolicy)
|
tx(TableName.SecretApprovalPolicy)
|
||||||
@@ -33,7 +34,17 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
void qb.where(`${TableName.SecretApprovalPolicy}.id`, "=", customFilter.sapId);
|
void qb.where(`${TableName.SecretApprovalPolicy}.id`, "=", customFilter.sapId);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.join(TableName.Environment, `${TableName.SecretApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
.join(
|
||||||
|
TableName.SecretApprovalPolicyEnvironment,
|
||||||
|
`${TableName.SecretApprovalPolicyEnvironment}.policyId`,
|
||||||
|
`${TableName.SecretApprovalPolicy}.id`
|
||||||
|
)
|
||||||
|
.join(TableName.Environment, `${TableName.SecretApprovalPolicyEnvironment}.envId`, `${TableName.Environment}.id`)
|
||||||
|
.where((qb) => {
|
||||||
|
if (customFilter?.envId) {
|
||||||
|
void qb.where(`${TableName.SecretApprovalPolicyEnvironment}.envId`, "=", customFilter.envId);
|
||||||
|
}
|
||||||
|
})
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.SecretApprovalPolicyApprover,
|
TableName.SecretApprovalPolicyApprover,
|
||||||
`${TableName.SecretApprovalPolicy}.id`,
|
`${TableName.SecretApprovalPolicy}.id`,
|
||||||
@@ -97,7 +108,7 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
.select(
|
.select(
|
||||||
tx.ref("name").withSchema(TableName.Environment).as("envName"),
|
tx.ref("name").withSchema(TableName.Environment).as("envName"),
|
||||||
tx.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
tx.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
||||||
tx.ref("id").withSchema(TableName.Environment).as("envId"),
|
tx.ref("id").withSchema(TableName.Environment).as("environmentId"),
|
||||||
tx.ref("projectId").withSchema(TableName.Environment)
|
tx.ref("projectId").withSchema(TableName.Environment)
|
||||||
)
|
)
|
||||||
.select(selectAllTableCols(TableName.SecretApprovalPolicy))
|
.select(selectAllTableCols(TableName.SecretApprovalPolicy))
|
||||||
@@ -146,6 +157,15 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
firstName,
|
firstName,
|
||||||
lastName
|
lastName
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "environmentId",
|
||||||
|
label: "environments" as const,
|
||||||
|
mapper: ({ environmentId, envName, envSlug }) => ({
|
||||||
|
id: environmentId,
|
||||||
|
name: envName,
|
||||||
|
slug: envSlug
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -160,6 +180,7 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
filter: TFindFilter<TSecretApprovalPolicies & { projectId: string }>,
|
filter: TFindFilter<TSecretApprovalPolicies & { projectId: string }>,
|
||||||
customFilter?: {
|
customFilter?: {
|
||||||
sapId?: string;
|
sapId?: string;
|
||||||
|
envId?: string;
|
||||||
},
|
},
|
||||||
tx?: Knex
|
tx?: Knex
|
||||||
) => {
|
) => {
|
||||||
@@ -221,6 +242,15 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
mapper: ({ approverGroupUserId: userId }) => ({
|
mapper: ({ approverGroupUserId: userId }) => ({
|
||||||
userId
|
userId
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "environmentId",
|
||||||
|
label: "environments" as const,
|
||||||
|
mapper: ({ environmentId, envName, envSlug }) => ({
|
||||||
|
id: environmentId,
|
||||||
|
name: envName,
|
||||||
|
slug: envSlug
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -235,5 +265,74 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
|
|||||||
return softDeletedPolicy;
|
return softDeletedPolicy;
|
||||||
};
|
};
|
||||||
|
|
||||||
return { ...secretApprovalPolicyOrm, findById, find, softDeleteById };
|
const findPolicyByEnvIdAndSecretPath = async (
|
||||||
|
{ envIds, secretPath }: { envIds: string[]; secretPath: string },
|
||||||
|
tx?: Knex
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const docs = await (tx || db.replicaNode())(TableName.SecretApprovalPolicy)
|
||||||
|
.join(
|
||||||
|
TableName.SecretApprovalPolicyEnvironment,
|
||||||
|
`${TableName.SecretApprovalPolicyEnvironment}.policyId`,
|
||||||
|
`${TableName.SecretApprovalPolicy}.id`
|
||||||
|
)
|
||||||
|
.join(
|
||||||
|
TableName.Environment,
|
||||||
|
`${TableName.SecretApprovalPolicyEnvironment}.envId`,
|
||||||
|
`${TableName.Environment}.id`
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
buildFindFilter(
|
||||||
|
{
|
||||||
|
$in: {
|
||||||
|
envId: envIds
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TableName.SecretApprovalPolicyEnvironment
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
buildFindFilter(
|
||||||
|
{
|
||||||
|
secretPath
|
||||||
|
},
|
||||||
|
TableName.SecretApprovalPolicy
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.whereNull(`${TableName.SecretApprovalPolicy}.deletedAt`)
|
||||||
|
.orderBy("deletedAt", "desc")
|
||||||
|
.orderByRaw(`"deletedAt" IS NULL`)
|
||||||
|
.select(selectAllTableCols(TableName.SecretApprovalPolicy))
|
||||||
|
.select(db.ref("name").withSchema(TableName.Environment).as("envName"))
|
||||||
|
.select(db.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||||
|
.select(db.ref("id").withSchema(TableName.Environment).as("environmentId"))
|
||||||
|
.select(db.ref("projectId").withSchema(TableName.Environment));
|
||||||
|
const formattedDocs = sqlNestRelationships({
|
||||||
|
data: docs,
|
||||||
|
key: "id",
|
||||||
|
parentMapper: (data) => ({
|
||||||
|
projectId: data.projectId,
|
||||||
|
...SecretApprovalPoliciesSchema.parse(data)
|
||||||
|
}),
|
||||||
|
childrenMapper: [
|
||||||
|
{
|
||||||
|
key: "environmentId",
|
||||||
|
label: "environments" as const,
|
||||||
|
mapper: ({ environmentId: id, envName, envSlug }) => ({
|
||||||
|
id,
|
||||||
|
name: envName,
|
||||||
|
slug: envSlug
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
return formattedDocs?.[0];
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "findPolicyByEnvIdAndSecretPath" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...secretApprovalPolicyOrm, findById, find, softDeleteById, findPolicyByEnvIdAndSecretPath };
|
||||||
};
|
};
|
||||||
|
@@ -0,0 +1,32 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TDbClient } from "@app/db";
|
||||||
|
import { TableName } from "@app/db/schemas";
|
||||||
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
|
import { buildFindFilter, ormify, selectAllTableCols } from "@app/lib/knex";
|
||||||
|
|
||||||
|
export type TSecretApprovalPolicyEnvironmentDALFactory = ReturnType<typeof secretApprovalPolicyEnvironmentDALFactory>;
|
||||||
|
|
||||||
|
export const secretApprovalPolicyEnvironmentDALFactory = (db: TDbClient) => {
|
||||||
|
const secretApprovalPolicyEnvironmentOrm = ormify(db, TableName.SecretApprovalPolicyEnvironment);
|
||||||
|
|
||||||
|
const findAvailablePoliciesByEnvId = async (envId: string, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const docs = await (tx || db.replicaNode())(TableName.SecretApprovalPolicyEnvironment)
|
||||||
|
.join(
|
||||||
|
TableName.SecretApprovalPolicy,
|
||||||
|
`${TableName.SecretApprovalPolicyEnvironment}.policyId`,
|
||||||
|
`${TableName.SecretApprovalPolicy}.id`
|
||||||
|
)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
.where(buildFindFilter({ envId }, TableName.SecretApprovalPolicyEnvironment))
|
||||||
|
.whereNull(`${TableName.SecretApprovalPolicy}.deletedAt`)
|
||||||
|
.select(selectAllTableCols(TableName.SecretApprovalPolicyEnvironment));
|
||||||
|
return docs;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "findAvailablePoliciesByEnvId" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...secretApprovalPolicyEnvironmentOrm, findAvailablePoliciesByEnvId };
|
||||||
|
};
|
@@ -1,6 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import picomatch from "picomatch";
|
import picomatch from "picomatch";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
@@ -18,6 +19,7 @@ import {
|
|||||||
TSecretApprovalPolicyBypasserDALFactory
|
TSecretApprovalPolicyBypasserDALFactory
|
||||||
} from "./secret-approval-policy-approver-dal";
|
} from "./secret-approval-policy-approver-dal";
|
||||||
import { TSecretApprovalPolicyDALFactory } from "./secret-approval-policy-dal";
|
import { TSecretApprovalPolicyDALFactory } from "./secret-approval-policy-dal";
|
||||||
|
import { TSecretApprovalPolicyEnvironmentDALFactory } from "./secret-approval-policy-environment-dal";
|
||||||
import {
|
import {
|
||||||
TCreateSapDTO,
|
TCreateSapDTO,
|
||||||
TDeleteSapDTO,
|
TDeleteSapDTO,
|
||||||
@@ -35,12 +37,13 @@ const getPolicyScore = (policy: { secretPath?: string | null }) =>
|
|||||||
type TSecretApprovalPolicyServiceFactoryDep = {
|
type TSecretApprovalPolicyServiceFactoryDep = {
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||||
secretApprovalPolicyDAL: TSecretApprovalPolicyDALFactory;
|
secretApprovalPolicyDAL: TSecretApprovalPolicyDALFactory;
|
||||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne" | "find">;
|
||||||
userDAL: Pick<TUserDALFactory, "find">;
|
userDAL: Pick<TUserDALFactory, "find">;
|
||||||
secretApprovalPolicyApproverDAL: TSecretApprovalPolicyApproverDALFactory;
|
secretApprovalPolicyApproverDAL: TSecretApprovalPolicyApproverDALFactory;
|
||||||
secretApprovalPolicyBypasserDAL: TSecretApprovalPolicyBypasserDALFactory;
|
secretApprovalPolicyBypasserDAL: TSecretApprovalPolicyBypasserDALFactory;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||||
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "update">;
|
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "update">;
|
||||||
|
secretApprovalPolicyEnvironmentDAL: TSecretApprovalPolicyEnvironmentDALFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TSecretApprovalPolicyServiceFactory = ReturnType<typeof secretApprovalPolicyServiceFactory>;
|
export type TSecretApprovalPolicyServiceFactory = ReturnType<typeof secretApprovalPolicyServiceFactory>;
|
||||||
@@ -50,27 +53,30 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
permissionService,
|
permissionService,
|
||||||
secretApprovalPolicyApproverDAL,
|
secretApprovalPolicyApproverDAL,
|
||||||
secretApprovalPolicyBypasserDAL,
|
secretApprovalPolicyBypasserDAL,
|
||||||
|
secretApprovalPolicyEnvironmentDAL,
|
||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
secretApprovalRequestDAL
|
secretApprovalRequestDAL
|
||||||
}: TSecretApprovalPolicyServiceFactoryDep) => {
|
}: TSecretApprovalPolicyServiceFactoryDep) => {
|
||||||
const $policyExists = async ({
|
const $policyExists = async ({
|
||||||
|
envIds,
|
||||||
envId,
|
envId,
|
||||||
secretPath,
|
secretPath,
|
||||||
policyId
|
policyId
|
||||||
}: {
|
}: {
|
||||||
envId: string;
|
envIds?: string[];
|
||||||
|
envId?: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
policyId?: string;
|
policyId?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const policy = await secretApprovalPolicyDAL
|
if (!envIds && !envId) {
|
||||||
.findOne({
|
throw new BadRequestError({ message: "At least one environment should be provided" });
|
||||||
envId,
|
}
|
||||||
secretPath,
|
const policy = await secretApprovalPolicyDAL.findPolicyByEnvIdAndSecretPath({
|
||||||
deletedAt: null
|
envIds: envId ? [envId] : envIds || [],
|
||||||
})
|
secretPath
|
||||||
.catch(() => null);
|
});
|
||||||
|
|
||||||
return policyId ? policy && policy.id !== policyId : Boolean(policy);
|
return policyId ? policy && policy.id !== policyId : Boolean(policy);
|
||||||
};
|
};
|
||||||
@@ -87,6 +93,7 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
projectId,
|
projectId,
|
||||||
secretPath,
|
secretPath,
|
||||||
environment,
|
environment,
|
||||||
|
environments,
|
||||||
enforcementLevel,
|
enforcementLevel,
|
||||||
allowedSelfApprovals
|
allowedSelfApprovals
|
||||||
}: TCreateSapDTO) => {
|
}: TCreateSapDTO) => {
|
||||||
@@ -110,7 +117,8 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
@@ -125,17 +133,23 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const env = await projectEnvDAL.findOne({ slug: environment, projectId });
|
const mergedEnvs = (environment ? [environment] : environments) || [];
|
||||||
if (!env) {
|
if (mergedEnvs.length === 0) {
|
||||||
throw new NotFoundError({
|
throw new BadRequestError({ message: "Must provide either environment or environments" });
|
||||||
message: `Environment with slug '${environment}' not found in project with ID ${projectId}`
|
}
|
||||||
});
|
const envs = await projectEnvDAL.find({ $in: { slug: mergedEnvs }, projectId });
|
||||||
|
if (!envs.length || envs.length !== mergedEnvs.length) {
|
||||||
|
const notFoundEnvs = mergedEnvs.filter((env) => !envs.find((el) => el.slug === env));
|
||||||
|
throw new NotFoundError({ message: `One or more environments not found: ${notFoundEnvs.join(", ")}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await $policyExists({ envId: env.id, secretPath })) {
|
for (const env of envs) {
|
||||||
throw new BadRequestError({
|
// eslint-disable-next-line no-await-in-loop
|
||||||
message: `A policy for secret path '${secretPath}' already exists in environment '${environment}'`
|
if (await $policyExists({ envId: env.id, secretPath })) {
|
||||||
});
|
throw new BadRequestError({
|
||||||
|
message: `A policy for secret path '${secretPath}' already exists in environment '${env.slug}'`
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let groupBypassers: string[] = [];
|
let groupBypassers: string[] = [];
|
||||||
@@ -179,7 +193,7 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
const secretApproval = await secretApprovalPolicyDAL.transaction(async (tx) => {
|
const secretApproval = await secretApprovalPolicyDAL.transaction(async (tx) => {
|
||||||
const doc = await secretApprovalPolicyDAL.create(
|
const doc = await secretApprovalPolicyDAL.create(
|
||||||
{
|
{
|
||||||
envId: env.id,
|
envId: envs[0].id,
|
||||||
approvals,
|
approvals,
|
||||||
secretPath,
|
secretPath,
|
||||||
name,
|
name,
|
||||||
@@ -188,6 +202,13 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
await secretApprovalPolicyEnvironmentDAL.insertMany(
|
||||||
|
envs.map((env) => ({
|
||||||
|
envId: env.id,
|
||||||
|
policyId: doc.id
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
let userApproverIds = userApprovers;
|
let userApproverIds = userApprovers;
|
||||||
if (userApproverNames.length) {
|
if (userApproverNames.length) {
|
||||||
@@ -251,12 +272,13 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
return doc;
|
return doc;
|
||||||
});
|
});
|
||||||
|
|
||||||
return { ...secretApproval, environment: env, projectId };
|
return { ...secretApproval, environments: envs, projectId, environment: envs[0] };
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateSecretApprovalPolicy = async ({
|
const updateSecretApprovalPolicy = async ({
|
||||||
approvers,
|
approvers,
|
||||||
bypassers,
|
bypassers,
|
||||||
|
environments,
|
||||||
secretPath,
|
secretPath,
|
||||||
name,
|
name,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -286,17 +308,26 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
message: `Secret approval policy with ID '${secretPolicyId}' not found`
|
message: `Secret approval policy with ID '${secretPolicyId}' not found`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
let envs = secretApprovalPolicy.environments;
|
||||||
if (
|
if (
|
||||||
await $policyExists({
|
environments &&
|
||||||
envId: secretApprovalPolicy.envId,
|
(environments.length !== envs.length || environments.some((env) => !envs.find((el) => el.slug === env)))
|
||||||
secretPath: secretPath || secretApprovalPolicy.secretPath,
|
|
||||||
policyId: secretApprovalPolicy.id
|
|
||||||
})
|
|
||||||
) {
|
) {
|
||||||
throw new BadRequestError({
|
envs = await projectEnvDAL.find({ $in: { slug: environments }, projectId: secretApprovalPolicy.projectId });
|
||||||
message: `A policy for secret path '${secretPath}' already exists in environment '${secretApprovalPolicy.environment.slug}'`
|
}
|
||||||
});
|
for (const env of envs) {
|
||||||
|
if (
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await $policyExists({
|
||||||
|
envId: env.id,
|
||||||
|
secretPath: secretPath || secretApprovalPolicy.secretPath,
|
||||||
|
policyId: secretApprovalPolicy.id
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `A policy for secret path '${secretPath || secretApprovalPolicy.secretPath}' already exists in environment '${env.slug}'`
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
@@ -304,7 +335,8 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: secretApprovalPolicy.projectId,
|
projectId: secretApprovalPolicy.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
||||||
|
|
||||||
@@ -412,6 +444,17 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (environments) {
|
||||||
|
await secretApprovalPolicyEnvironmentDAL.delete({ policyId: doc.id }, tx);
|
||||||
|
await secretApprovalPolicyEnvironmentDAL.insertMany(
|
||||||
|
envs.map((env) => ({
|
||||||
|
envId: env.id,
|
||||||
|
policyId: doc.id
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
await secretApprovalPolicyBypasserDAL.delete({ policyId: doc.id }, tx);
|
await secretApprovalPolicyBypasserDAL.delete({ policyId: doc.id }, tx);
|
||||||
|
|
||||||
if (bypasserUserIds.length) {
|
if (bypasserUserIds.length) {
|
||||||
@@ -438,7 +481,8 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
...updatedSap,
|
...updatedSap,
|
||||||
environment: secretApprovalPolicy.environment,
|
environments: secretApprovalPolicy.environments,
|
||||||
|
environment: secretApprovalPolicy.environments[0],
|
||||||
projectId: secretApprovalPolicy.projectId
|
projectId: secretApprovalPolicy.projectId
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -459,7 +503,8 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: sapPolicy.projectId,
|
projectId: sapPolicy.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Delete,
|
ProjectPermissionActions.Delete,
|
||||||
@@ -483,7 +528,12 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
const updatedPolicy = await secretApprovalPolicyDAL.softDeleteById(secretPolicyId, tx);
|
const updatedPolicy = await secretApprovalPolicyDAL.softDeleteById(secretPolicyId, tx);
|
||||||
return updatedPolicy;
|
return updatedPolicy;
|
||||||
});
|
});
|
||||||
return { ...deletedPolicy, projectId: sapPolicy.projectId, environment: sapPolicy.environment };
|
return {
|
||||||
|
...deletedPolicy,
|
||||||
|
projectId: sapPolicy.projectId,
|
||||||
|
environments: sapPolicy.environments,
|
||||||
|
environment: sapPolicy.environments[0]
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSecretApprovalPolicyByProjectId = async ({
|
const getSecretApprovalPolicyByProjectId = async ({
|
||||||
@@ -498,7 +548,8 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
||||||
|
|
||||||
@@ -515,7 +566,7 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const policies = await secretApprovalPolicyDAL.find({ envId: env.id, deletedAt: null });
|
const policies = await secretApprovalPolicyDAL.find({ deletedAt: null }, { envId: env.id });
|
||||||
if (!policies.length) return;
|
if (!policies.length) return;
|
||||||
// this will filter policies either without scoped to secret path or the one that matches with secret path
|
// this will filter policies either without scoped to secret path or the one that matches with secret path
|
||||||
const policiesFilteredByPath = policies.filter(
|
const policiesFilteredByPath = policies.filter(
|
||||||
@@ -542,7 +593,8 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
return getSecretApprovalPolicy(projectId, environment, secretPath);
|
return getSecretApprovalPolicy(projectId, environment, secretPath);
|
||||||
@@ -568,7 +620,8 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: sapPolicy.projectId,
|
projectId: sapPolicy.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
||||||
|
@@ -5,7 +5,8 @@ import { ApproverType, BypasserType } from "../access-approval-policy/access-app
|
|||||||
export type TCreateSapDTO = {
|
export type TCreateSapDTO = {
|
||||||
approvals: number;
|
approvals: number;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
environment: string;
|
environment?: string;
|
||||||
|
environments?: string[];
|
||||||
approvers: ({ type: ApproverType.Group; id: string } | { type: ApproverType.User; id?: string; username?: string })[];
|
approvers: ({ type: ApproverType.Group; id: string } | { type: ApproverType.User; id?: string; username?: string })[];
|
||||||
bypassers?: (
|
bypassers?: (
|
||||||
| { type: BypasserType.Group; id: string }
|
| { type: BypasserType.Group; id: string }
|
||||||
@@ -29,6 +30,7 @@ export type TUpdateSapDTO = {
|
|||||||
name?: string;
|
name?: string;
|
||||||
enforcementLevel?: EnforcementLevel;
|
enforcementLevel?: EnforcementLevel;
|
||||||
allowedSelfApprovals?: boolean;
|
allowedSelfApprovals?: boolean;
|
||||||
|
environments?: string[];
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TDeleteSapDTO = {
|
export type TDeleteSapDTO = {
|
||||||
|
@@ -40,6 +40,13 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretApprovalRequest}.policyId`,
|
`${TableName.SecretApprovalRequest}.policyId`,
|
||||||
`${TableName.SecretApprovalPolicy}.id`
|
`${TableName.SecretApprovalPolicy}.id`
|
||||||
)
|
)
|
||||||
|
.leftJoin(TableName.SecretApprovalPolicyEnvironment, (bd) => {
|
||||||
|
bd.on(
|
||||||
|
`${TableName.SecretApprovalPolicy}.id`,
|
||||||
|
"=",
|
||||||
|
`${TableName.SecretApprovalPolicyEnvironment}.policyId`
|
||||||
|
).andOn(`${TableName.SecretApprovalPolicyEnvironment}.envId`, "=", `${TableName.SecretFolder}.envId`);
|
||||||
|
})
|
||||||
.leftJoin<TUsers>(
|
.leftJoin<TUsers>(
|
||||||
db(TableName.Users).as("statusChangedByUser"),
|
db(TableName.Users).as("statusChangedByUser"),
|
||||||
`${TableName.SecretApprovalRequest}.statusChangedByUserId`,
|
`${TableName.SecretApprovalRequest}.statusChangedByUserId`,
|
||||||
@@ -146,7 +153,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
tx.ref("projectId").withSchema(TableName.Environment),
|
tx.ref("projectId").withSchema(TableName.Environment),
|
||||||
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
|
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
|
||||||
tx.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
tx.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
|
||||||
tx.ref("envId").withSchema(TableName.SecretApprovalPolicy).as("policyEnvId"),
|
tx.ref("envId").withSchema(TableName.SecretApprovalPolicyEnvironment).as("policyEnvId"),
|
||||||
tx.ref("enforcementLevel").withSchema(TableName.SecretApprovalPolicy).as("policyEnforcementLevel"),
|
tx.ref("enforcementLevel").withSchema(TableName.SecretApprovalPolicy).as("policyEnforcementLevel"),
|
||||||
tx.ref("allowedSelfApprovals").withSchema(TableName.SecretApprovalPolicy).as("policyAllowedSelfApprovals"),
|
tx.ref("allowedSelfApprovals").withSchema(TableName.SecretApprovalPolicy).as("policyAllowedSelfApprovals"),
|
||||||
tx.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
|
tx.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
|
||||||
|
@@ -36,7 +36,7 @@ export const sendApprovalEmailsFn = async ({
|
|||||||
firstName: reviewerUser.firstName,
|
firstName: reviewerUser.firstName,
|
||||||
projectName: project.name,
|
projectName: project.name,
|
||||||
organizationName: project.organization.name,
|
organizationName: project.organization.name,
|
||||||
approvalUrl: `${cfg.SITE_URL}/projects/${project.id}/secret-manager/approval?requestId=${secretApprovalRequest.id}`
|
approvalUrl: `${cfg.SITE_URL}/projects/secret-management/${project.id}/approval?requestId=${secretApprovalRequest.id}`
|
||||||
},
|
},
|
||||||
template: SmtpTemplates.SecretApprovalRequestNeedsReview
|
template: SmtpTemplates.SecretApprovalRequestNeedsReview
|
||||||
});
|
});
|
||||||
|
@@ -3,6 +3,7 @@ import { ForbiddenError, subject } from "@casl/ability";
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ActionProjectType,
|
||||||
ProjectMembershipRole,
|
ProjectMembershipRole,
|
||||||
SecretEncryptionAlgo,
|
SecretEncryptionAlgo,
|
||||||
SecretKeyEncoding,
|
SecretKeyEncoding,
|
||||||
@@ -184,7 +185,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, actorId, policyId);
|
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, actorId, policyId);
|
||||||
@@ -211,7 +213,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const { shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
const { shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
||||||
@@ -263,7 +266,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
@@ -412,7 +416,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: secretApprovalRequest.projectId,
|
projectId: secretApprovalRequest.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
@@ -481,7 +486,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: secretApprovalRequest.projectId,
|
projectId: secretApprovalRequest.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
@@ -531,13 +537,19 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
message: "The policy associated with this secret approval request has been deleted."
|
message: "The policy associated with this secret approval request has been deleted."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (!policy.envId) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "The policy associated with this secret approval request is not linked to the environment."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const { hasRole } = await permissionService.getProjectPermission({
|
const { hasRole } = await permissionService.getProjectPermission({
|
||||||
actor: ActorType.USER,
|
actor: ActorType.USER,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -955,7 +967,7 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
bypassReason,
|
bypassReason,
|
||||||
secretPath: policy.secretPath,
|
secretPath: policy.secretPath,
|
||||||
environment: env.name,
|
environment: env.name,
|
||||||
approvalUrl: `${cfg.SITE_URL}/projects/${project.id}/secret-manager/approval`
|
approvalUrl: `${cfg.SITE_URL}/projects/secret-management/${project.id}/approval`
|
||||||
},
|
},
|
||||||
template: SmtpTemplates.AccessSecretRequestBypassed
|
template: SmtpTemplates.AccessSecretRequestBypassed
|
||||||
});
|
});
|
||||||
@@ -1089,7 +1101,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.ReadValue, {
|
throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.ReadValue, {
|
||||||
@@ -1380,7 +1393,8 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
||||||
if (!folder)
|
if (!folder)
|
||||||
|
@@ -167,7 +167,7 @@ export const secretRotationV2QueueServiceFactory = async ({
|
|||||||
environment: environment.name,
|
environment: environment.name,
|
||||||
projectName: project.name,
|
projectName: project.name,
|
||||||
rotationUrl: encodeURI(
|
rotationUrl: encodeURI(
|
||||||
`${appCfg.SITE_URL}/projects/${projectId}/secret-manager/secrets/${environment.slug}`
|
`${appCfg.SITE_URL}/projects/secret-management/${projectId}/secrets/${environment.slug}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -2,7 +2,7 @@ import { ForbiddenError, subject } from "@casl/ability";
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
import isEqual from "lodash.isequal";
|
import isEqual from "lodash.isequal";
|
||||||
|
|
||||||
import { SecretType, TableName } from "@app/db/schemas";
|
import { ActionProjectType, SecretType, TableName } from "@app/db/schemas";
|
||||||
import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType, TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
@@ -223,7 +223,7 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -274,7 +274,7 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -320,7 +320,7 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -385,7 +385,7 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -429,7 +429,7 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -631,7 +631,7 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -781,7 +781,7 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1113,7 +1113,7 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1160,7 +1160,7 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1212,7 +1212,7 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1328,7 +1328,8 @@ export const secretRotationV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
const permissiveFolderMappings = folderMappings.filter(({ path, environment }) =>
|
const permissiveFolderMappings = folderMappings.filter(({ path, environment }) =>
|
||||||
|
@@ -7,12 +7,13 @@ import {
|
|||||||
TRotationFactoryRevokeCredentials,
|
TRotationFactoryRevokeCredentials,
|
||||||
TRotationFactoryRotateCredentials
|
TRotationFactoryRotateCredentials
|
||||||
} from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-types";
|
} from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-types";
|
||||||
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
import {
|
import {
|
||||||
executeWithPotentialGateway,
|
executeWithPotentialGateway,
|
||||||
SQL_CONNECTION_ALTER_LOGIN_STATEMENT
|
SQL_CONNECTION_ALTER_LOGIN_STATEMENT
|
||||||
} from "@app/services/app-connection/shared/sql";
|
} from "@app/services/app-connection/shared/sql";
|
||||||
|
|
||||||
import { generatePassword } from "../utils";
|
import { DEFAULT_PASSWORD_REQUIREMENTS, generatePassword } from "../utils";
|
||||||
import {
|
import {
|
||||||
TSqlCredentialsRotationGeneratedCredentials,
|
TSqlCredentialsRotationGeneratedCredentials,
|
||||||
TSqlCredentialsRotationWithConnection
|
TSqlCredentialsRotationWithConnection
|
||||||
@@ -32,6 +33,11 @@ const redactPasswords = (e: unknown, credentials: TSqlCredentialsRotationGenerat
|
|||||||
return redactedMessage;
|
return redactedMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ORACLE_PASSWORD_REQUIREMENTS = {
|
||||||
|
...DEFAULT_PASSWORD_REQUIREMENTS,
|
||||||
|
length: 30
|
||||||
|
};
|
||||||
|
|
||||||
export const sqlCredentialsRotationFactory: TRotationFactory<
|
export const sqlCredentialsRotationFactory: TRotationFactory<
|
||||||
TSqlCredentialsRotationWithConnection,
|
TSqlCredentialsRotationWithConnection,
|
||||||
TSqlCredentialsRotationGeneratedCredentials
|
TSqlCredentialsRotationGeneratedCredentials
|
||||||
@@ -43,6 +49,9 @@ export const sqlCredentialsRotationFactory: TRotationFactory<
|
|||||||
secretsMapping
|
secretsMapping
|
||||||
} = secretRotation;
|
} = secretRotation;
|
||||||
|
|
||||||
|
const passwordRequirement =
|
||||||
|
connection.app === AppConnection.OracleDB ? ORACLE_PASSWORD_REQUIREMENTS : DEFAULT_PASSWORD_REQUIREMENTS;
|
||||||
|
|
||||||
const executeOperation = <T>(
|
const executeOperation = <T>(
|
||||||
operation: (client: Knex) => Promise<T>,
|
operation: (client: Knex) => Promise<T>,
|
||||||
credentialsOverride?: TSqlCredentialsRotationGeneratedCredentials[number]
|
credentialsOverride?: TSqlCredentialsRotationGeneratedCredentials[number]
|
||||||
@@ -65,7 +74,7 @@ export const sqlCredentialsRotationFactory: TRotationFactory<
|
|||||||
const $validateCredentials = async (credentials: TSqlCredentialsRotationGeneratedCredentials[number]) => {
|
const $validateCredentials = async (credentials: TSqlCredentialsRotationGeneratedCredentials[number]) => {
|
||||||
try {
|
try {
|
||||||
await executeOperation(async (client) => {
|
await executeOperation(async (client) => {
|
||||||
await client.raw("SELECT 1");
|
await client.raw(connection.app === AppConnection.OracleDB ? `SELECT 1 FROM DUAL` : `Select 1`);
|
||||||
}, credentials);
|
}, credentials);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(redactPasswords(error, [credentials]));
|
throw new Error(redactPasswords(error, [credentials]));
|
||||||
@@ -75,11 +84,13 @@ export const sqlCredentialsRotationFactory: TRotationFactory<
|
|||||||
const issueCredentials: TRotationFactoryIssueCredentials<TSqlCredentialsRotationGeneratedCredentials> = async (
|
const issueCredentials: TRotationFactoryIssueCredentials<TSqlCredentialsRotationGeneratedCredentials> = async (
|
||||||
callback
|
callback
|
||||||
) => {
|
) => {
|
||||||
|
// For SQL, since we get existing users, we change both their passwords
|
||||||
|
// on issue to invalidate their existing passwords
|
||||||
// For SQL, since we get existing users, we change both their passwords
|
// For SQL, since we get existing users, we change both their passwords
|
||||||
// on issue to invalidate their existing passwords
|
// on issue to invalidate their existing passwords
|
||||||
const credentialsSet = [
|
const credentialsSet = [
|
||||||
{ username: username1, password: generatePassword() },
|
{ username: username1, password: generatePassword(passwordRequirement) },
|
||||||
{ username: username2, password: generatePassword() }
|
{ username: username2, password: generatePassword(passwordRequirement) }
|
||||||
];
|
];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -105,7 +116,10 @@ export const sqlCredentialsRotationFactory: TRotationFactory<
|
|||||||
credentialsToRevoke,
|
credentialsToRevoke,
|
||||||
callback
|
callback
|
||||||
) => {
|
) => {
|
||||||
const revokedCredentials = credentialsToRevoke.map(({ username }) => ({ username, password: generatePassword() }));
|
const revokedCredentials = credentialsToRevoke.map(({ username }) => ({
|
||||||
|
username,
|
||||||
|
password: generatePassword(passwordRequirement)
|
||||||
|
}));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await executeOperation(async (client) => {
|
await executeOperation(async (client) => {
|
||||||
@@ -128,7 +142,10 @@ export const sqlCredentialsRotationFactory: TRotationFactory<
|
|||||||
callback
|
callback
|
||||||
) => {
|
) => {
|
||||||
// generate new password for the next active user
|
// generate new password for the next active user
|
||||||
const credentials = { username: activeIndex === 0 ? username2 : username1, password: generatePassword() };
|
const credentials = {
|
||||||
|
username: activeIndex === 0 ? username2 : username1,
|
||||||
|
password: generatePassword(passwordRequirement)
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await executeOperation(async (client) => {
|
await executeOperation(async (client) => {
|
||||||
|
@@ -11,7 +11,7 @@ type TPasswordRequirements = {
|
|||||||
allowedSymbols?: string;
|
allowedSymbols?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_PASSWORD_REQUIREMENTS: TPasswordRequirements = {
|
export const DEFAULT_PASSWORD_REQUIREMENTS: TPasswordRequirements = {
|
||||||
length: 48,
|
length: 48,
|
||||||
required: {
|
required: {
|
||||||
lowercase: 1,
|
lowercase: 1,
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
import Ajv from "ajv";
|
import Ajv from "ajv";
|
||||||
|
|
||||||
import { ProjectVersion, TableName } from "@app/db/schemas";
|
import { ActionProjectType, ProjectVersion, TableName } from "@app/db/schemas";
|
||||||
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { TProjectPermission } from "@app/lib/types";
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
@@ -66,7 +66,8 @@ export const secretRotationServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionSecretRotationActions.Read,
|
ProjectPermissionSecretRotationActions.Read,
|
||||||
@@ -97,7 +98,8 @@ export const secretRotationServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionSecretRotationActions.Read,
|
ProjectPermissionSecretRotationActions.Read,
|
||||||
@@ -213,7 +215,8 @@ export const secretRotationServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionSecretRotationActions.Read,
|
ProjectPermissionSecretRotationActions.Read,
|
||||||
@@ -263,7 +266,8 @@ export const secretRotationServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionSecretRotationActions.Edit,
|
ProjectPermissionSecretRotationActions.Edit,
|
||||||
@@ -283,7 +287,8 @@ export const secretRotationServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: doc.projectId,
|
projectId: doc.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionSecretRotationActions.Delete,
|
ProjectPermissionSecretRotationActions.Delete,
|
||||||
|
@@ -596,7 +596,7 @@ export const secretScanningV2QueueServiceFactory = async ({
|
|||||||
numberOfSecrets: payload.numberOfSecrets,
|
numberOfSecrets: payload.numberOfSecrets,
|
||||||
isDiffScan: payload.isDiffScan,
|
isDiffScan: payload.isDiffScan,
|
||||||
url: encodeURI(
|
url: encodeURI(
|
||||||
`${appCfg.SITE_URL}/projects/${projectId}/secret-scanning/findings?search=scanId:${payload.scanId}`
|
`${appCfg.SITE_URL}/projects/secret-scanning/${projectId}/findings?search=scanId:${payload.scanId}`
|
||||||
),
|
),
|
||||||
timestamp
|
timestamp
|
||||||
}
|
}
|
||||||
@@ -607,7 +607,7 @@ export const secretScanningV2QueueServiceFactory = async ({
|
|||||||
timestamp,
|
timestamp,
|
||||||
errorMessage: payload.errorMessage,
|
errorMessage: payload.errorMessage,
|
||||||
url: encodeURI(
|
url: encodeURI(
|
||||||
`${appCfg.SITE_URL}/projects/${projectId}/secret-scanning/data-sources/${dataSource.type}/${dataSource.id}`
|
`${appCfg.SITE_URL}/projects/secret-scanning/${projectId}/data-sources/${dataSource.type}/${dataSource.id}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import {
|
import {
|
||||||
@@ -94,7 +95,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -156,7 +157,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId: dataSource.projectId
|
projectId: dataSource.projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -201,7 +202,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -235,7 +236,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId: payload.projectId
|
projectId: payload.projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -348,7 +349,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId: dataSource.projectId
|
projectId: dataSource.projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -401,6 +402,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId: dataSource.projectId
|
projectId: dataSource.projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -474,7 +476,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId: dataSource.projectId
|
projectId: dataSource.projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -538,7 +540,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId: dataSource.projectId
|
projectId: dataSource.projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -583,7 +585,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId: dataSource.projectId
|
projectId: dataSource.projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -626,7 +628,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId: dataSource.projectId
|
projectId: dataSource.projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -669,7 +671,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId: dataSource.projectId
|
projectId: dataSource.projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -702,7 +704,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -736,7 +738,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -776,7 +778,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId: finding.projectId
|
projectId: finding.projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -807,7 +809,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -842,7 +844,7 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
actorId: actor.id,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actorOrgId: actor.orgId,
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretScanning,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
// akhilmhdh: I did this, quite strange bug with eslint. Everything do have a type stil has this error
|
// akhilmhdh: I did this, quite strange bug with eslint. Everything do have a type stil has this error
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
import { TableName, TSecretTagJunctionInsert, TSecretV2TagJunctionInsert } from "@app/db/schemas";
|
import { ActionProjectType, TableName, TSecretTagJunctionInsert, TSecretV2TagJunctionInsert } from "@app/db/schemas";
|
||||||
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
|
||||||
import { InternalServerError, NotFoundError } from "@app/lib/errors";
|
import { InternalServerError, NotFoundError } from "@app/lib/errors";
|
||||||
import { groupBy } from "@app/lib/fn";
|
import { groupBy } from "@app/lib/fn";
|
||||||
@@ -103,7 +103,8 @@ export const secretSnapshotServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||||
|
|
||||||
@@ -139,7 +140,8 @@ export const secretSnapshotServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||||
|
|
||||||
@@ -167,7 +169,8 @@ export const secretSnapshotServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: snapshot.projectId,
|
projectId: snapshot.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
|
||||||
@@ -391,7 +394,8 @@ export const secretSnapshotServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: snapshot.projectId,
|
projectId: snapshot.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
@@ -58,7 +59,8 @@ export const sshCertificateTemplateServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -130,7 +132,8 @@ export const sshCertificateTemplateServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: certTemplate.projectId,
|
projectId: certTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -198,7 +201,8 @@ export const sshCertificateTemplateServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: certificateTemplate.projectId,
|
projectId: certificateTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -224,7 +228,8 @@ export const sshCertificateTemplateServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: certTemplate.projectId,
|
projectId: certTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { TSshHostDALFactory } from "@app/ee/services/ssh-host/ssh-host-dal";
|
import { TSshHostDALFactory } from "@app/ee/services/ssh-host/ssh-host-dal";
|
||||||
@@ -79,7 +80,8 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.SshHostGroups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.SshHostGroups);
|
||||||
@@ -171,7 +173,8 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: sshHostGroup.projectId,
|
projectId: sshHostGroup.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SshHostGroups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SshHostGroups);
|
||||||
@@ -267,7 +270,8 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: sshHostGroup.projectId,
|
projectId: sshHostGroup.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshHostGroups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshHostGroups);
|
||||||
@@ -290,7 +294,8 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: sshHostGroup.projectId,
|
projectId: sshHostGroup.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.SshHostGroups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.SshHostGroups);
|
||||||
@@ -316,7 +321,8 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: sshHostGroup.projectId,
|
projectId: sshHostGroup.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshHostGroups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshHostGroups);
|
||||||
@@ -354,7 +360,8 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: sshHostGroup.projectId,
|
projectId: sshHostGroup.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SshHostGroups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SshHostGroups);
|
||||||
@@ -393,7 +400,8 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: sshHostGroup.projectId,
|
projectId: sshHostGroup.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SshHostGroups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SshHostGroups);
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
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 { ProjectPermissionSshHostActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||||
@@ -62,7 +63,8 @@ export const createSshLoginMappings = async ({
|
|||||||
userId: user.id,
|
userId: user.id,
|
||||||
projectId,
|
projectId,
|
||||||
authMethod: actorAuthMethod,
|
authMethod: actorAuthMethod,
|
||||||
userOrgId: actorOrgId
|
userOrgId: actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { ProjectPermissionSshHostActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionSshHostActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
@@ -111,7 +112,8 @@ export const sshHostServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectHosts = await sshHostDAL.findUserAccessibleSshHosts([project.id], actorId);
|
const projectHosts = await sshHostDAL.findUserAccessibleSshHosts([project.id], actorId);
|
||||||
@@ -144,7 +146,8 @@ export const sshHostServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -273,7 +276,8 @@ export const sshHostServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: host.projectId,
|
projectId: host.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -334,7 +338,8 @@ export const sshHostServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: host.projectId,
|
projectId: host.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -362,7 +367,8 @@ export const sshHostServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: host.projectId,
|
projectId: host.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -401,7 +407,8 @@ export const sshHostServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: host.projectId,
|
projectId: host.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
const internalPrincipals = await convertActorToPrincipals({
|
const internalPrincipals = await convertActorToPrincipals({
|
||||||
@@ -520,7 +527,8 @@ export const sshHostServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: host.projectId,
|
projectId: host.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { TSshCertificateAuthorityDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-dal";
|
import { TSshCertificateAuthorityDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-dal";
|
||||||
@@ -72,7 +73,8 @@ export const sshCertificateAuthorityServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -107,7 +109,8 @@ export const sshCertificateAuthorityServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -175,7 +178,8 @@ export const sshCertificateAuthorityServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -213,7 +217,8 @@ export const sshCertificateAuthorityServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -254,7 +259,8 @@ export const sshCertificateAuthorityServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: sshCertificateTemplate.projectId,
|
projectId: sshCertificateTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -375,7 +381,8 @@ export const sshCertificateAuthorityServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: sshCertificateTemplate.projectId,
|
projectId: sshCertificateTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@@ -472,7 +479,8 @@ export const sshCertificateAuthorityServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId: ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SSH
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
@@ -35,7 +36,8 @@ export const trustedIpServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
||||||
const trustedIps = await trustedIpDAL.find({
|
const trustedIps = await trustedIpDAL.find({
|
||||||
@@ -59,7 +61,8 @@ export const trustedIpServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
|
||||||
|
|
||||||
@@ -104,7 +107,8 @@ export const trustedIpServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
|
||||||
|
|
||||||
@@ -149,7 +153,8 @@ export const trustedIpServiceFactory = ({
|
|||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
|
||||||
|
|
||||||
|
@@ -2245,7 +2245,9 @@ 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.",
|
||||||
|
clientId: "The Client ID to use to connect with Azure Client Secrets.",
|
||||||
|
clientSecret: "The Client Secret to use to connect with Azure Client Secrets."
|
||||||
},
|
},
|
||||||
AZURE_DEVOPS: {
|
AZURE_DEVOPS: {
|
||||||
code: "The OAuth code to use to connect with Azure DevOps.",
|
code: "The OAuth code to use to connect with Azure DevOps.",
|
||||||
@@ -2290,6 +2292,9 @@ export const AppConnections = {
|
|||||||
accessKey: "The Key used to access Supabase.",
|
accessKey: "The Key used to access Supabase.",
|
||||||
instanceUrl: "The URL used to access Supabase."
|
instanceUrl: "The URL used to access Supabase."
|
||||||
},
|
},
|
||||||
|
DIGITAL_OCEAN_APP_PLATFORM: {
|
||||||
|
apiToken: "The API token used to authenticate with Digital Ocean App Platform."
|
||||||
|
},
|
||||||
OKTA: {
|
OKTA: {
|
||||||
instanceUrl: "The URL used to access your Okta organization.",
|
instanceUrl: "The URL used to access your Okta organization.",
|
||||||
apiToken: "The API token used to authenticate with Okta."
|
apiToken: "The API token used to authenticate with Okta."
|
||||||
@@ -2370,6 +2375,10 @@ export const SecretSyncs = {
|
|||||||
keyId: "The AWS KMS key ID or alias to use when encrypting parameters synced by Infisical.",
|
keyId: "The AWS KMS key ID or alias to use when encrypting parameters synced by Infisical.",
|
||||||
tags: "Optional tags to add to secrets synced by Infisical.",
|
tags: "Optional tags to add to secrets synced by Infisical.",
|
||||||
syncSecretMetadataAsTags: `Whether Infisical secret metadata should be added as tags to secrets synced by Infisical.`
|
syncSecretMetadataAsTags: `Whether Infisical secret metadata should be added as tags to secrets synced by Infisical.`
|
||||||
|
},
|
||||||
|
RENDER: {
|
||||||
|
autoRedeployServices:
|
||||||
|
"Whether Infisical should automatically redeploy the configured Render service upon secret changes."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DESTINATION_CONFIG: {
|
DESTINATION_CONFIG: {
|
||||||
@@ -2506,6 +2515,11 @@ export const SecretSyncs = {
|
|||||||
SUPABASE: {
|
SUPABASE: {
|
||||||
projectId: "The ID of the Supabase project to sync secrets to.",
|
projectId: "The ID of the Supabase project to sync secrets to.",
|
||||||
projectName: "The name of the Supabase project to sync secrets to."
|
projectName: "The name of the Supabase project to sync secrets to."
|
||||||
|
},
|
||||||
|
BITBUCKET: {
|
||||||
|
workspaceSlug: "The Bitbucket Workspace slug to sync secrets to.",
|
||||||
|
repositorySlug: "The Bitbucket Repository slug to sync secrets to.",
|
||||||
|
environmentId: "The Bitbucket Deployment Environment uuid to sync secrets to."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -261,10 +261,26 @@ const envSchema = z
|
|||||||
// gcp app
|
// gcp app
|
||||||
INF_APP_CONNECTION_GCP_SERVICE_ACCOUNT_CREDENTIAL: zpStr(z.string().optional()),
|
INF_APP_CONNECTION_GCP_SERVICE_ACCOUNT_CREDENTIAL: zpStr(z.string().optional()),
|
||||||
|
|
||||||
// azure app
|
// Legacy Single Multi Purpose Azure App Connection
|
||||||
INF_APP_CONNECTION_AZURE_CLIENT_ID: zpStr(z.string().optional()),
|
INF_APP_CONNECTION_AZURE_CLIENT_ID: zpStr(z.string().optional()),
|
||||||
INF_APP_CONNECTION_AZURE_CLIENT_SECRET: zpStr(z.string().optional()),
|
INF_APP_CONNECTION_AZURE_CLIENT_SECRET: zpStr(z.string().optional()),
|
||||||
|
|
||||||
|
// Azure App Configuration App Connection
|
||||||
|
INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_ID: zpStr(z.string().optional()),
|
||||||
|
INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_SECRET: zpStr(z.string().optional()),
|
||||||
|
|
||||||
|
// Azure Key Vault App Connection
|
||||||
|
INF_APP_CONNECTION_AZURE_KEY_VAULT_CLIENT_ID: zpStr(z.string().optional()),
|
||||||
|
INF_APP_CONNECTION_AZURE_KEY_VAULT_CLIENT_SECRET: zpStr(z.string().optional()),
|
||||||
|
|
||||||
|
// Azure Client Secrets App Connection
|
||||||
|
INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_ID: zpStr(z.string().optional()),
|
||||||
|
INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_SECRET: zpStr(z.string().optional()),
|
||||||
|
|
||||||
|
// Azure DevOps App Connection
|
||||||
|
INF_APP_CONNECTION_AZURE_DEVOPS_CLIENT_ID: zpStr(z.string().optional()),
|
||||||
|
INF_APP_CONNECTION_AZURE_DEVOPS_CLIENT_SECRET: zpStr(z.string().optional()),
|
||||||
|
|
||||||
// datadog
|
// datadog
|
||||||
SHOULD_USE_DATADOG_TRACER: zodStrBool.default("false"),
|
SHOULD_USE_DATADOG_TRACER: zodStrBool.default("false"),
|
||||||
DATADOG_PROFILING_ENABLED: zodStrBool.default("false"),
|
DATADOG_PROFILING_ENABLED: zodStrBool.default("false"),
|
||||||
@@ -341,7 +357,23 @@ const envSchema = z
|
|||||||
isHsmConfigured:
|
isHsmConfigured:
|
||||||
Boolean(data.HSM_LIB_PATH) && Boolean(data.HSM_PIN) && Boolean(data.HSM_KEY_LABEL) && data.HSM_SLOT !== undefined,
|
Boolean(data.HSM_LIB_PATH) && Boolean(data.HSM_PIN) && Boolean(data.HSM_KEY_LABEL) && data.HSM_SLOT !== undefined,
|
||||||
samlDefaultOrgSlug: data.DEFAULT_SAML_ORG_SLUG,
|
samlDefaultOrgSlug: data.DEFAULT_SAML_ORG_SLUG,
|
||||||
SECRET_SCANNING_ORG_WHITELIST: data.SECRET_SCANNING_ORG_WHITELIST?.split(",")
|
SECRET_SCANNING_ORG_WHITELIST: data.SECRET_SCANNING_ORG_WHITELIST?.split(","),
|
||||||
|
INF_APP_CONNECTION_AZURE_DEVOPS_CLIENT_ID:
|
||||||
|
data.INF_APP_CONNECTION_AZURE_DEVOPS_CLIENT_ID || data.INF_APP_CONNECTION_AZURE_CLIENT_ID,
|
||||||
|
INF_APP_CONNECTION_AZURE_DEVOPS_CLIENT_SECRET:
|
||||||
|
data.INF_APP_CONNECTION_AZURE_DEVOPS_CLIENT_SECRET || data.INF_APP_CONNECTION_AZURE_CLIENT_SECRET,
|
||||||
|
INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_ID:
|
||||||
|
data.INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_ID || data.INF_APP_CONNECTION_AZURE_CLIENT_ID,
|
||||||
|
INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_SECRET:
|
||||||
|
data.INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_SECRET || data.INF_APP_CONNECTION_AZURE_CLIENT_SECRET,
|
||||||
|
INF_APP_CONNECTION_AZURE_KEY_VAULT_CLIENT_ID:
|
||||||
|
data.INF_APP_CONNECTION_AZURE_KEY_VAULT_CLIENT_ID || data.INF_APP_CONNECTION_AZURE_CLIENT_ID,
|
||||||
|
INF_APP_CONNECTION_AZURE_KEY_VAULT_CLIENT_SECRET:
|
||||||
|
data.INF_APP_CONNECTION_AZURE_KEY_VAULT_CLIENT_SECRET || data.INF_APP_CONNECTION_AZURE_CLIENT_SECRET,
|
||||||
|
INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_ID:
|
||||||
|
data.INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_ID || data.INF_APP_CONNECTION_AZURE_CLIENT_ID,
|
||||||
|
INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_SECRET:
|
||||||
|
data.INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_SECRET || data.INF_APP_CONNECTION_AZURE_CLIENT_SECRET
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export type TEnvConfig = Readonly<z.infer<typeof envSchema>>;
|
export type TEnvConfig = Readonly<z.infer<typeof envSchema>>;
|
||||||
@@ -451,15 +483,54 @@ export const overwriteSchema: {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
azure: {
|
azureAppConfiguration: {
|
||||||
name: "Azure",
|
name: "Azure App Configuration",
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
key: "INF_APP_CONNECTION_AZURE_CLIENT_ID",
|
key: "INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_ID",
|
||||||
description: "The Application (Client) ID of your Azure application."
|
description: "The Application (Client) ID of your Azure application."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "INF_APP_CONNECTION_AZURE_CLIENT_SECRET",
|
key: "INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_SECRET",
|
||||||
|
description: "The Client Secret of your Azure application."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
azureKeyVault: {
|
||||||
|
name: "Azure Key Vault",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
key: "INF_APP_CONNECTION_AZURE_KEY_VAULT_CLIENT_ID",
|
||||||
|
description: "The Application (Client) ID of your Azure application."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "INF_APP_CONNECTION_AZURE_KEY_VAULT_CLIENT_SECRET",
|
||||||
|
description: "The Client Secret of your Azure application."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
azureClientSecrets: {
|
||||||
|
name: "Azure Client Secrets",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
key: "INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_ID",
|
||||||
|
description: "The Application (Client) ID of your Azure application."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_SECRET",
|
||||||
|
description: "The Client Secret of your Azure application."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
azureDevOps: {
|
||||||
|
name: "Azure DevOps",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
key: "INF_APP_CONNECTION_AZURE_DEVOPS_CLIENT_ID",
|
||||||
|
description: "The Application (Client) ID of your Azure application."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "INF_APP_CONNECTION_AZURE_DEVOPS_CLIENT_SECRET",
|
||||||
description: "The Client Secret of your Azure application."
|
description: "The Client Secret of your Azure application."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@@ -14,7 +14,7 @@ import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal
|
|||||||
import { ADMIN_CONFIG_DB_UUID } from "@app/services/super-admin/super-admin-service";
|
import { ADMIN_CONFIG_DB_UUID } from "@app/services/super-admin/super-admin-service";
|
||||||
|
|
||||||
import { isBase64 } from "../../base64";
|
import { isBase64 } from "../../base64";
|
||||||
import { getConfig } from "../../config/env";
|
import { getConfig, TEnvConfig } from "../../config/env";
|
||||||
import { CryptographyError } from "../../errors";
|
import { CryptographyError } from "../../errors";
|
||||||
import { logger } from "../../logger";
|
import { logger } from "../../logger";
|
||||||
import { asymmetricFipsValidated } from "./asymmetric-fips";
|
import { asymmetricFipsValidated } from "./asymmetric-fips";
|
||||||
@@ -106,12 +106,12 @@ const cryptographyFactory = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const $setFipsModeEnabled = (enabled: boolean) => {
|
const $setFipsModeEnabled = (enabled: boolean, envCfg?: Pick<TEnvConfig, "ENCRYPTION_KEY">) => {
|
||||||
// If FIPS is enabled, we need to validate that the ENCRYPTION_KEY is in a base64 format, and is a 256-bit key.
|
// If FIPS is enabled, we need to validate that the ENCRYPTION_KEY is in a base64 format, and is a 256-bit key.
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
crypto.setFips(true);
|
crypto.setFips(true);
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = envCfg || getConfig();
|
||||||
|
|
||||||
if (appCfg.ENCRYPTION_KEY) {
|
if (appCfg.ENCRYPTION_KEY) {
|
||||||
// we need to validate that the ENCRYPTION_KEY is a base64 encoded 256-bit key
|
// we need to validate that the ENCRYPTION_KEY is a base64 encoded 256-bit key
|
||||||
@@ -141,14 +141,14 @@ const cryptographyFactory = () => {
|
|||||||
$isInitialized = true;
|
$isInitialized = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialize = async (superAdminDAL: TSuperAdminDALFactory) => {
|
const initialize = async (superAdminDAL: TSuperAdminDALFactory, envCfg?: Pick<TEnvConfig, "ENCRYPTION_KEY">) => {
|
||||||
if ($isInitialized) {
|
if ($isInitialized) {
|
||||||
return isFipsModeEnabled();
|
return isFipsModeEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.FIPS_ENABLED !== "true") {
|
if (process.env.FIPS_ENABLED !== "true") {
|
||||||
logger.info("Cryptography module initialized in normal operation mode.");
|
logger.info("Cryptography module initialized in normal operation mode.");
|
||||||
$setFipsModeEnabled(false);
|
$setFipsModeEnabled(false, envCfg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,11 +158,11 @@ const cryptographyFactory = () => {
|
|||||||
if (serverCfg) {
|
if (serverCfg) {
|
||||||
if (serverCfg.fipsEnabled) {
|
if (serverCfg.fipsEnabled) {
|
||||||
logger.info("[FIPS]: Instance is configured for FIPS mode of operation. Continuing startup with FIPS enabled.");
|
logger.info("[FIPS]: Instance is configured for FIPS mode of operation. Continuing startup with FIPS enabled.");
|
||||||
$setFipsModeEnabled(true);
|
$setFipsModeEnabled(true, envCfg);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
logger.info("[FIPS]: Instance age predates FIPS mode inception date. Continuing without FIPS.");
|
logger.info("[FIPS]: Instance age predates FIPS mode inception date. Continuing without FIPS.");
|
||||||
$setFipsModeEnabled(false);
|
$setFipsModeEnabled(false, envCfg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +171,7 @@ const cryptographyFactory = () => {
|
|||||||
// TODO(daniel): check if it's an enterprise deployment
|
// TODO(daniel): check if it's an enterprise deployment
|
||||||
|
|
||||||
// if there is no server cfg, and FIPS_MODE is `true`, its a fresh FIPS deployment. We need to set the fipsEnabled to true.
|
// if there is no server cfg, and FIPS_MODE is `true`, its a fresh FIPS deployment. We need to set the fipsEnabled to true.
|
||||||
$setFipsModeEnabled(true);
|
$setFipsModeEnabled(true, envCfg);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -64,7 +64,9 @@ export enum QueueName {
|
|||||||
FolderTreeCheckpoint = "folder-tree-checkpoint",
|
FolderTreeCheckpoint = "folder-tree-checkpoint",
|
||||||
InvalidateCache = "invalidate-cache",
|
InvalidateCache = "invalidate-cache",
|
||||||
SecretScanningV2 = "secret-scanning-v2",
|
SecretScanningV2 = "secret-scanning-v2",
|
||||||
TelemetryAggregatedEvents = "telemetry-aggregated-events"
|
TelemetryAggregatedEvents = "telemetry-aggregated-events",
|
||||||
|
DailyReminders = "daily-reminders",
|
||||||
|
SecretReminderMigration = "secret-reminder-migration"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum QueueJobs {
|
export enum QueueJobs {
|
||||||
@@ -104,7 +106,9 @@ export enum QueueJobs {
|
|||||||
SecretScanningV2SendNotification = "secret-scanning-v2-notification",
|
SecretScanningV2SendNotification = "secret-scanning-v2-notification",
|
||||||
CaOrderCertificateForSubscriber = "ca-order-certificate-for-subscriber",
|
CaOrderCertificateForSubscriber = "ca-order-certificate-for-subscriber",
|
||||||
PkiSubscriberDailyAutoRenewal = "pki-subscriber-daily-auto-renewal",
|
PkiSubscriberDailyAutoRenewal = "pki-subscriber-daily-auto-renewal",
|
||||||
TelemetryAggregatedEvents = "telemetry-aggregated-events"
|
TelemetryAggregatedEvents = "telemetry-aggregated-events",
|
||||||
|
DailyReminders = "daily-reminders",
|
||||||
|
SecretReminderMigration = "secret-reminder-migration"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TQueueJobTypes = {
|
export type TQueueJobTypes = {
|
||||||
@@ -291,6 +295,14 @@ export type TQueueJobTypes = {
|
|||||||
caType: CaType;
|
caType: CaType;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
[QueueName.DailyReminders]: {
|
||||||
|
name: QueueJobs.DailyReminders;
|
||||||
|
payload: undefined;
|
||||||
|
};
|
||||||
|
[QueueName.SecretReminderMigration]: {
|
||||||
|
name: QueueJobs.SecretReminderMigration;
|
||||||
|
payload: undefined;
|
||||||
|
};
|
||||||
[QueueName.PkiSubscriber]: {
|
[QueueName.PkiSubscriber]: {
|
||||||
name: QueueJobs.PkiSubscriberDailyAutoRenewal;
|
name: QueueJobs.PkiSubscriberDailyAutoRenewal;
|
||||||
payload: undefined;
|
payload: undefined;
|
||||||
@@ -390,6 +402,11 @@ export type TQueueServiceFactory = {
|
|||||||
startOffset?: number,
|
startOffset?: number,
|
||||||
endOffset?: number
|
endOffset?: number
|
||||||
) => Promise<{ key: string; name: string; id: string | null }[]>;
|
) => Promise<{ key: string; name: string; id: string | null }[]>;
|
||||||
|
getDelayedJobs: (
|
||||||
|
name: QueueName,
|
||||||
|
startOffset?: number,
|
||||||
|
endOffset?: number
|
||||||
|
) => Promise<{ delay: number; timestamp: number; repeatJobKey?: string; data?: unknown }[]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const queueServiceFactory = (
|
export const queueServiceFactory = (
|
||||||
@@ -552,6 +569,13 @@ export const queueServiceFactory = (
|
|||||||
return q.getRepeatableJobs(startOffset, endOffset);
|
return q.getRepeatableJobs(startOffset, endOffset);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getDelayedJobs: TQueueServiceFactory["getDelayedJobs"] = (name, startOffset, endOffset) => {
|
||||||
|
const q = queueContainer[name];
|
||||||
|
if (!q) throw new Error(`Queue '${name}' not initialized`);
|
||||||
|
|
||||||
|
return q.getDelayed(startOffset, endOffset);
|
||||||
|
};
|
||||||
|
|
||||||
const stopRepeatableJobByJobId: TQueueServiceFactory["stopRepeatableJobByJobId"] = async (name, jobId) => {
|
const stopRepeatableJobByJobId: TQueueServiceFactory["stopRepeatableJobByJobId"] = async (name, jobId) => {
|
||||||
const q = queueContainer[name];
|
const q = queueContainer[name];
|
||||||
const job = await q.getJob(jobId);
|
const job = await q.getJob(jobId);
|
||||||
@@ -598,6 +622,7 @@ export const queueServiceFactory = (
|
|||||||
stopJobById,
|
stopJobById,
|
||||||
stopJobByIdPg,
|
stopJobByIdPg,
|
||||||
getRepeatableJobs,
|
getRepeatableJobs,
|
||||||
|
getDelayedJobs,
|
||||||
startPg,
|
startPg,
|
||||||
queuePg,
|
queuePg,
|
||||||
schedulePg
|
schedulePg
|
||||||
|
@@ -162,6 +162,12 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => {
|
|||||||
kubernetes: token?.identityAuth?.kubernetes
|
kubernetes: token?.identityAuth?.kubernetes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (token?.identityAuth?.aws) {
|
||||||
|
requestContext.set("identityAuthInfo", {
|
||||||
|
identityId: identity.identityId,
|
||||||
|
aws: token?.identityAuth?.aws
|
||||||
|
});
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AuthMode.SERVICE_TOKEN: {
|
case AuthMode.SERVICE_TOKEN: {
|
||||||
|
@@ -11,6 +11,7 @@ import {
|
|||||||
accessApprovalPolicyBypasserDALFactory
|
accessApprovalPolicyBypasserDALFactory
|
||||||
} from "@app/ee/services/access-approval-policy/access-approval-policy-approver-dal";
|
} from "@app/ee/services/access-approval-policy/access-approval-policy-approver-dal";
|
||||||
import { accessApprovalPolicyDALFactory } from "@app/ee/services/access-approval-policy/access-approval-policy-dal";
|
import { accessApprovalPolicyDALFactory } from "@app/ee/services/access-approval-policy/access-approval-policy-dal";
|
||||||
|
import { accessApprovalPolicyEnvironmentDALFactory } from "@app/ee/services/access-approval-policy/access-approval-policy-environment-dal";
|
||||||
import { accessApprovalPolicyServiceFactory } from "@app/ee/services/access-approval-policy/access-approval-policy-service";
|
import { accessApprovalPolicyServiceFactory } from "@app/ee/services/access-approval-policy/access-approval-policy-service";
|
||||||
import { accessApprovalRequestDALFactory } from "@app/ee/services/access-approval-request/access-approval-request-dal";
|
import { accessApprovalRequestDALFactory } from "@app/ee/services/access-approval-request/access-approval-request-dal";
|
||||||
import { accessApprovalRequestReviewerDALFactory } from "@app/ee/services/access-approval-request/access-approval-request-reviewer-dal";
|
import { accessApprovalRequestReviewerDALFactory } from "@app/ee/services/access-approval-request/access-approval-request-reviewer-dal";
|
||||||
@@ -76,6 +77,7 @@ import {
|
|||||||
secretApprovalPolicyBypasserDALFactory
|
secretApprovalPolicyBypasserDALFactory
|
||||||
} from "@app/ee/services/secret-approval-policy/secret-approval-policy-approver-dal";
|
} from "@app/ee/services/secret-approval-policy/secret-approval-policy-approver-dal";
|
||||||
import { secretApprovalPolicyDALFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-dal";
|
import { secretApprovalPolicyDALFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-dal";
|
||||||
|
import { secretApprovalPolicyEnvironmentDALFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-environment-dal";
|
||||||
import { secretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
|
import { secretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
|
||||||
import { secretApprovalRequestDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-dal";
|
import { secretApprovalRequestDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-dal";
|
||||||
import { secretApprovalRequestReviewerDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-reviewer-dal";
|
import { secretApprovalRequestReviewerDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-reviewer-dal";
|
||||||
@@ -246,6 +248,10 @@ import { projectMembershipServiceFactory } from "@app/services/project-membershi
|
|||||||
import { projectUserMembershipRoleDALFactory } from "@app/services/project-membership/project-user-membership-role-dal";
|
import { projectUserMembershipRoleDALFactory } from "@app/services/project-membership/project-user-membership-role-dal";
|
||||||
import { projectRoleDALFactory } from "@app/services/project-role/project-role-dal";
|
import { projectRoleDALFactory } from "@app/services/project-role/project-role-dal";
|
||||||
import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service";
|
import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service";
|
||||||
|
import { reminderDALFactory } from "@app/services/reminder/reminder-dal";
|
||||||
|
import { dailyReminderQueueServiceFactory } from "@app/services/reminder/reminder-queue";
|
||||||
|
import { reminderServiceFactory } from "@app/services/reminder/reminder-service";
|
||||||
|
import { reminderRecipientDALFactory } from "@app/services/reminder-recipients/reminder-recipient-dal";
|
||||||
import { dailyResourceCleanUpQueueServiceFactory } from "@app/services/resource-cleanup/resource-cleanup-queue";
|
import { dailyResourceCleanUpQueueServiceFactory } from "@app/services/resource-cleanup/resource-cleanup-queue";
|
||||||
import { resourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
|
import { resourceMetadataDALFactory } from "@app/services/resource-metadata/resource-metadata-dal";
|
||||||
import { secretDALFactory } from "@app/services/secret/secret-dal";
|
import { secretDALFactory } from "@app/services/secret/secret-dal";
|
||||||
@@ -371,6 +377,9 @@ export const registerRoutes = async (
|
|||||||
const secretVersionV2BridgeDAL = secretVersionV2BridgeDALFactory(db);
|
const secretVersionV2BridgeDAL = secretVersionV2BridgeDALFactory(db);
|
||||||
const secretVersionTagV2BridgeDAL = secretVersionV2TagBridgeDALFactory(db);
|
const secretVersionTagV2BridgeDAL = secretVersionV2TagBridgeDALFactory(db);
|
||||||
|
|
||||||
|
const reminderDAL = reminderDALFactory(db);
|
||||||
|
const reminderRecipientDAL = reminderRecipientDALFactory(db);
|
||||||
|
|
||||||
const integrationDAL = integrationDALFactory(db);
|
const integrationDAL = integrationDALFactory(db);
|
||||||
const integrationAuthDAL = integrationAuthDALFactory(db);
|
const integrationAuthDAL = integrationAuthDALFactory(db);
|
||||||
const webhookDAL = webhookDALFactory(db);
|
const webhookDAL = webhookDALFactory(db);
|
||||||
@@ -418,9 +427,11 @@ export const registerRoutes = async (
|
|||||||
const accessApprovalPolicyApproverDAL = accessApprovalPolicyApproverDALFactory(db);
|
const accessApprovalPolicyApproverDAL = accessApprovalPolicyApproverDALFactory(db);
|
||||||
const accessApprovalPolicyBypasserDAL = accessApprovalPolicyBypasserDALFactory(db);
|
const accessApprovalPolicyBypasserDAL = accessApprovalPolicyBypasserDALFactory(db);
|
||||||
const accessApprovalRequestReviewerDAL = accessApprovalRequestReviewerDALFactory(db);
|
const accessApprovalRequestReviewerDAL = accessApprovalRequestReviewerDALFactory(db);
|
||||||
|
const accessApprovalPolicyEnvironmentDAL = accessApprovalPolicyEnvironmentDALFactory(db);
|
||||||
|
|
||||||
const sapApproverDAL = secretApprovalPolicyApproverDALFactory(db);
|
const sapApproverDAL = secretApprovalPolicyApproverDALFactory(db);
|
||||||
const sapBypasserDAL = secretApprovalPolicyBypasserDALFactory(db);
|
const sapBypasserDAL = secretApprovalPolicyBypasserDALFactory(db);
|
||||||
|
const sapEnvironmentDAL = secretApprovalPolicyEnvironmentDALFactory(db);
|
||||||
const secretApprovalPolicyDAL = secretApprovalPolicyDALFactory(db);
|
const secretApprovalPolicyDAL = secretApprovalPolicyDALFactory(db);
|
||||||
const secretApprovalRequestDAL = secretApprovalRequestDALFactory(db);
|
const secretApprovalRequestDAL = secretApprovalRequestDALFactory(db);
|
||||||
const secretApprovalRequestReviewerDAL = secretApprovalRequestReviewerDALFactory(db);
|
const secretApprovalRequestReviewerDAL = secretApprovalRequestReviewerDALFactory(db);
|
||||||
@@ -554,6 +565,7 @@ export const registerRoutes = async (
|
|||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
secretApprovalPolicyApproverDAL: sapApproverDAL,
|
secretApprovalPolicyApproverDAL: sapApproverDAL,
|
||||||
secretApprovalPolicyBypasserDAL: sapBypasserDAL,
|
secretApprovalPolicyBypasserDAL: sapBypasserDAL,
|
||||||
|
secretApprovalPolicyEnvironmentDAL: sapEnvironmentDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
secretApprovalPolicyDAL,
|
secretApprovalPolicyDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
@@ -734,9 +746,17 @@ export const registerRoutes = async (
|
|||||||
|
|
||||||
const projectBotService = projectBotServiceFactory({ permissionService, projectBotDAL, projectDAL });
|
const projectBotService = projectBotServiceFactory({ permissionService, projectBotDAL, projectDAL });
|
||||||
|
|
||||||
|
const reminderService = reminderServiceFactory({
|
||||||
|
reminderDAL,
|
||||||
|
reminderRecipientDAL,
|
||||||
|
smtpService,
|
||||||
|
projectMembershipDAL,
|
||||||
|
permissionService,
|
||||||
|
secretV2BridgeDAL
|
||||||
|
});
|
||||||
|
|
||||||
const orgService = orgServiceFactory({
|
const orgService = orgServiceFactory({
|
||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
queueService,
|
|
||||||
identityMetadataDAL,
|
identityMetadataDAL,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
@@ -762,7 +782,8 @@ export const registerRoutes = async (
|
|||||||
orgBotDAL,
|
orgBotDAL,
|
||||||
oidcConfigDAL,
|
oidcConfigDAL,
|
||||||
loginService,
|
loginService,
|
||||||
projectBotService
|
projectBotService,
|
||||||
|
reminderService
|
||||||
});
|
});
|
||||||
const signupService = authSignupServiceFactory({
|
const signupService = authSignupServiceFactory({
|
||||||
tokenService,
|
tokenService,
|
||||||
@@ -1060,7 +1081,6 @@ export const registerRoutes = async (
|
|||||||
secretImportDAL,
|
secretImportDAL,
|
||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
webhookDAL,
|
webhookDAL,
|
||||||
orgDAL,
|
|
||||||
auditLogService,
|
auditLogService,
|
||||||
userDAL,
|
userDAL,
|
||||||
projectMembershipDAL,
|
projectMembershipDAL,
|
||||||
@@ -1082,11 +1102,11 @@ export const registerRoutes = async (
|
|||||||
secretApprovalRequestDAL,
|
secretApprovalRequestDAL,
|
||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectUserMembershipRoleDAL,
|
projectUserMembershipRoleDAL,
|
||||||
secretReminderRecipientsDAL,
|
|
||||||
orgService,
|
orgService,
|
||||||
resourceMetadataDAL,
|
resourceMetadataDAL,
|
||||||
folderCommitService,
|
folderCommitService,
|
||||||
secretSyncQueue
|
secretSyncQueue,
|
||||||
|
reminderService
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectService = projectServiceFactory({
|
const projectService = projectServiceFactory({
|
||||||
@@ -1095,7 +1115,6 @@ export const registerRoutes = async (
|
|||||||
projectSshConfigDAL,
|
projectSshConfigDAL,
|
||||||
secretDAL,
|
secretDAL,
|
||||||
secretV2BridgeDAL,
|
secretV2BridgeDAL,
|
||||||
queueService,
|
|
||||||
projectQueue: projectQueueService,
|
projectQueue: projectQueueService,
|
||||||
projectBotService,
|
projectBotService,
|
||||||
identityProjectDAL,
|
identityProjectDAL,
|
||||||
@@ -1132,7 +1151,8 @@ export const registerRoutes = async (
|
|||||||
microsoftTeamsIntegrationDAL,
|
microsoftTeamsIntegrationDAL,
|
||||||
projectTemplateService,
|
projectTemplateService,
|
||||||
groupProjectDAL,
|
groupProjectDAL,
|
||||||
smtpService
|
smtpService,
|
||||||
|
reminderService
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectEnvService = projectEnvServiceFactory({
|
const projectEnvService = projectEnvServiceFactory({
|
||||||
@@ -1141,7 +1161,9 @@ export const registerRoutes = async (
|
|||||||
keyStore,
|
keyStore,
|
||||||
licenseService,
|
licenseService,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
folderDAL
|
folderDAL,
|
||||||
|
accessApprovalPolicyEnvironmentDAL,
|
||||||
|
secretApprovalPolicyEnvironmentDAL: sapEnvironmentDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectRoleService = projectRoleServiceFactory({
|
const projectRoleService = projectRoleServiceFactory({
|
||||||
@@ -1231,6 +1253,7 @@ export const registerRoutes = async (
|
|||||||
kmsService,
|
kmsService,
|
||||||
snapshotService,
|
snapshotService,
|
||||||
resourceMetadataDAL,
|
resourceMetadataDAL,
|
||||||
|
reminderService,
|
||||||
keyStore
|
keyStore
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1284,7 +1307,8 @@ export const registerRoutes = async (
|
|||||||
secretApprovalRequestSecretDAL,
|
secretApprovalRequestSecretDAL,
|
||||||
secretV2BridgeService,
|
secretV2BridgeService,
|
||||||
secretApprovalRequestService,
|
secretApprovalRequestService,
|
||||||
licenseService
|
licenseService,
|
||||||
|
reminderService
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretSharingService = secretSharingServiceFactory({
|
const secretSharingService = secretSharingServiceFactory({
|
||||||
@@ -1300,6 +1324,7 @@ export const registerRoutes = async (
|
|||||||
accessApprovalPolicyDAL,
|
accessApprovalPolicyDAL,
|
||||||
accessApprovalPolicyApproverDAL,
|
accessApprovalPolicyApproverDAL,
|
||||||
accessApprovalPolicyBypasserDAL,
|
accessApprovalPolicyBypasserDAL,
|
||||||
|
accessApprovalPolicyEnvironmentDAL,
|
||||||
groupDAL,
|
groupDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
@@ -1616,7 +1641,6 @@ export const registerRoutes = async (
|
|||||||
auditLogDAL,
|
auditLogDAL,
|
||||||
queueService,
|
queueService,
|
||||||
secretVersionDAL,
|
secretVersionDAL,
|
||||||
secretDAL,
|
|
||||||
secretFolderVersionDAL: folderVersionDAL,
|
secretFolderVersionDAL: folderVersionDAL,
|
||||||
snapshotDAL,
|
snapshotDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
@@ -1627,6 +1651,13 @@ export const registerRoutes = async (
|
|||||||
orgService
|
orgService
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const dailyReminderQueueService = dailyReminderQueueServiceFactory({
|
||||||
|
reminderService,
|
||||||
|
queueService,
|
||||||
|
secretDAL: secretV2BridgeDAL,
|
||||||
|
secretReminderRecipientsDAL
|
||||||
|
});
|
||||||
|
|
||||||
const dailyExpiringPkiItemAlert = dailyExpiringPkiItemAlertQueueServiceFactory({
|
const dailyExpiringPkiItemAlert = dailyExpiringPkiItemAlertQueueServiceFactory({
|
||||||
queueService,
|
queueService,
|
||||||
pkiAlertService
|
pkiAlertService
|
||||||
@@ -1926,6 +1957,8 @@ export const registerRoutes = async (
|
|||||||
await telemetryQueue.startTelemetryCheck();
|
await telemetryQueue.startTelemetryCheck();
|
||||||
await telemetryQueue.startAggregatedEventsJob();
|
await telemetryQueue.startAggregatedEventsJob();
|
||||||
await dailyResourceCleanUp.startCleanUp();
|
await dailyResourceCleanUp.startCleanUp();
|
||||||
|
await dailyReminderQueueService.startDailyRemindersJob();
|
||||||
|
await dailyReminderQueueService.startSecretReminderMigrationJob();
|
||||||
await dailyExpiringPkiItemAlert.startSendingAlerts();
|
await dailyExpiringPkiItemAlert.startSendingAlerts();
|
||||||
await pkiSubscriberQueue.startDailyAutoRenewalJob();
|
await pkiSubscriberQueue.startDailyAutoRenewalJob();
|
||||||
await kmsService.startService();
|
await kmsService.startService();
|
||||||
@@ -2036,7 +2069,8 @@ export const registerRoutes = async (
|
|||||||
assumePrivileges: assumePrivilegeService,
|
assumePrivileges: assumePrivilegeService,
|
||||||
githubOrgSync: githubOrgSyncConfigService,
|
githubOrgSync: githubOrgSyncConfigService,
|
||||||
folderCommit: folderCommitService,
|
folderCommit: folderCommitService,
|
||||||
secretScanningV2: secretScanningV2Service
|
secretScanningV2: secretScanningV2Service,
|
||||||
|
reminder: reminderService
|
||||||
});
|
});
|
||||||
|
|
||||||
const cronJobs: CronJob[] = [];
|
const cronJobs: CronJob[] = [];
|
||||||
|
@@ -93,6 +93,13 @@ export const sapPubSchema = SecretApprovalPoliciesSchema.merge(
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
slug: z.string()
|
slug: z.string()
|
||||||
}),
|
}),
|
||||||
|
environments: z.array(
|
||||||
|
z.object({
|
||||||
|
id: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
slug: z.string()
|
||||||
|
})
|
||||||
|
),
|
||||||
projectId: z.string()
|
projectId: z.string()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@@ -51,6 +51,10 @@ import {
|
|||||||
DatabricksConnectionListItemSchema,
|
DatabricksConnectionListItemSchema,
|
||||||
SanitizedDatabricksConnectionSchema
|
SanitizedDatabricksConnectionSchema
|
||||||
} from "@app/services/app-connection/databricks";
|
} from "@app/services/app-connection/databricks";
|
||||||
|
import {
|
||||||
|
DigitalOceanConnectionListItemSchema,
|
||||||
|
SanitizedDigitalOceanConnectionSchema
|
||||||
|
} from "@app/services/app-connection/digital-ocean";
|
||||||
import { FlyioConnectionListItemSchema, SanitizedFlyioConnectionSchema } from "@app/services/app-connection/flyio";
|
import { FlyioConnectionListItemSchema, SanitizedFlyioConnectionSchema } from "@app/services/app-connection/flyio";
|
||||||
import { GcpConnectionListItemSchema, SanitizedGcpConnectionSchema } from "@app/services/app-connection/gcp";
|
import { GcpConnectionListItemSchema, SanitizedGcpConnectionSchema } from "@app/services/app-connection/gcp";
|
||||||
import { GitHubConnectionListItemSchema, SanitizedGitHubConnectionSchema } from "@app/services/app-connection/github";
|
import { GitHubConnectionListItemSchema, SanitizedGitHubConnectionSchema } from "@app/services/app-connection/github";
|
||||||
@@ -140,6 +144,7 @@ const SanitizedAppConnectionSchema = z.union([
|
|||||||
...SanitizedRailwayConnectionSchema.options,
|
...SanitizedRailwayConnectionSchema.options,
|
||||||
...SanitizedChecklyConnectionSchema.options,
|
...SanitizedChecklyConnectionSchema.options,
|
||||||
...SanitizedSupabaseConnectionSchema.options,
|
...SanitizedSupabaseConnectionSchema.options,
|
||||||
|
...SanitizedDigitalOceanConnectionSchema.options,
|
||||||
...SanitizedOktaConnectionSchema.options
|
...SanitizedOktaConnectionSchema.options
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -178,6 +183,7 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
|||||||
RailwayConnectionListItemSchema,
|
RailwayConnectionListItemSchema,
|
||||||
ChecklyConnectionListItemSchema,
|
ChecklyConnectionListItemSchema,
|
||||||
SupabaseConnectionListItemSchema,
|
SupabaseConnectionListItemSchema,
|
||||||
|
DigitalOceanConnectionListItemSchema,
|
||||||
OktaConnectionListItemSchema
|
OktaConnectionListItemSchema
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -85,4 +85,40 @@ export const registerBitbucketConnectionRouter = async (server: FastifyZodProvid
|
|||||||
return { repositories };
|
return { repositories };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: `/:connectionId/environments`,
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
connectionId: z.string().uuid()
|
||||||
|
}),
|
||||||
|
querystring: z.object({
|
||||||
|
workspaceSlug: z.string().min(1).max(255),
|
||||||
|
repositorySlug: z.string().min(1).max(255)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
environments: z.object({ slug: z.string(), name: z.string(), uuid: z.string() }).array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const {
|
||||||
|
params: { connectionId },
|
||||||
|
query: { workspaceSlug, repositorySlug }
|
||||||
|
} = req;
|
||||||
|
|
||||||
|
const environments = await server.services.appConnection.bitbucket.listEnvironments(
|
||||||
|
{ connectionId, workspaceSlug, repositorySlug },
|
||||||
|
req.permission
|
||||||
|
);
|
||||||
|
|
||||||
|
return { environments };
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@@ -0,0 +1,57 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
|
import {
|
||||||
|
CreateDigitalOceanConnectionSchema,
|
||||||
|
SanitizedDigitalOceanConnectionSchema,
|
||||||
|
UpdateDigitalOceanConnectionSchema
|
||||||
|
} from "@app/services/app-connection/digital-ocean";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||||
|
|
||||||
|
export const registerDigitalOceanConnectionRouter = async (server: FastifyZodProvider) => {
|
||||||
|
registerAppConnectionEndpoints({
|
||||||
|
app: AppConnection.DigitalOcean,
|
||||||
|
server,
|
||||||
|
createSchema: CreateDigitalOceanConnectionSchema,
|
||||||
|
updateSchema: UpdateDigitalOceanConnectionSchema,
|
||||||
|
sanitizedResponseSchema: SanitizedDigitalOceanConnectionSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
// The below endpoints are not exposed and for Infisical App use
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: `/:connectionId/apps`,
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
connectionId: z.string().uuid()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
apps: z
|
||||||
|
.object({
|
||||||
|
id: z.string(),
|
||||||
|
spec: z.object({
|
||||||
|
name: z.string()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { connectionId } = req.params;
|
||||||
|
|
||||||
|
const apps = await server.services.appConnection.digitalOcean.listApps(connectionId, req.permission);
|
||||||
|
|
||||||
|
return { apps };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@@ -14,6 +14,7 @@ import { registerCamundaConnectionRouter } from "./camunda-connection-router";
|
|||||||
import { registerChecklyConnectionRouter } from "./checkly-connection-router";
|
import { registerChecklyConnectionRouter } from "./checkly-connection-router";
|
||||||
import { registerCloudflareConnectionRouter } from "./cloudflare-connection-router";
|
import { registerCloudflareConnectionRouter } from "./cloudflare-connection-router";
|
||||||
import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
|
import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
|
||||||
|
import { registerDigitalOceanConnectionRouter } from "./digital-ocean-connection-router";
|
||||||
import { registerFlyioConnectionRouter } from "./flyio-connection-router";
|
import { registerFlyioConnectionRouter } from "./flyio-connection-router";
|
||||||
import { registerGcpConnectionRouter } from "./gcp-connection-router";
|
import { registerGcpConnectionRouter } from "./gcp-connection-router";
|
||||||
import { registerGitHubConnectionRouter } from "./github-connection-router";
|
import { registerGitHubConnectionRouter } from "./github-connection-router";
|
||||||
@@ -74,5 +75,6 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server:
|
|||||||
[AppConnection.Railway]: registerRailwayConnectionRouter,
|
[AppConnection.Railway]: registerRailwayConnectionRouter,
|
||||||
[AppConnection.Checkly]: registerChecklyConnectionRouter,
|
[AppConnection.Checkly]: registerChecklyConnectionRouter,
|
||||||
[AppConnection.Supabase]: registerSupabaseConnectionRouter,
|
[AppConnection.Supabase]: registerSupabaseConnectionRouter,
|
||||||
|
[AppConnection.DigitalOcean]: registerDigitalOceanConnectionRouter,
|
||||||
[AppConnection.Okta]: registerOktaConnectionRouter
|
[AppConnection.Okta]: registerOktaConnectionRouter
|
||||||
};
|
};
|
||||||
|
@@ -270,11 +270,6 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
remainingLimit -= imports.length;
|
|
||||||
adjustedOffset = 0;
|
|
||||||
} else {
|
|
||||||
adjustedOffset = Math.max(0, adjustedOffset - totalImportCount);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,7 +312,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!includeDynamicSecrets && !includeSecrets)
|
if (!includeDynamicSecrets && !includeSecrets && !includeSecretRotations)
|
||||||
return {
|
return {
|
||||||
folders,
|
folders,
|
||||||
totalFolderCount,
|
totalFolderCount,
|
||||||
@@ -547,7 +542,6 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
(totalFolderCount ?? 0) +
|
(totalFolderCount ?? 0) +
|
||||||
(totalDynamicSecretCount ?? 0) +
|
(totalDynamicSecretCount ?? 0) +
|
||||||
(totalSecretCount ?? 0) +
|
(totalSecretCount ?? 0) +
|
||||||
(totalImportCount ?? 0) +
|
|
||||||
(totalSecretRotationCount ?? 0)
|
(totalSecretRotationCount ?? 0)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -904,7 +898,9 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
projectId,
|
projectId,
|
||||||
path: secretPath,
|
path: secretPath,
|
||||||
search,
|
search,
|
||||||
tagSlugs: tags
|
tagSlugs: tags,
|
||||||
|
includeTagsInSearch: true,
|
||||||
|
includeMetadataInSearch: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (remainingLimit > 0 && totalSecretCount > adjustedOffset) {
|
if (remainingLimit > 0 && totalSecretCount > adjustedOffset) {
|
||||||
@@ -924,7 +920,9 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
search,
|
search,
|
||||||
limit: remainingLimit,
|
limit: remainingLimit,
|
||||||
offset: adjustedOffset,
|
offset: adjustedOffset,
|
||||||
tagSlugs: tags
|
tagSlugs: tags,
|
||||||
|
includeTagsInSearch: true,
|
||||||
|
includeMetadataInSearch: true
|
||||||
})
|
})
|
||||||
).secrets;
|
).secrets;
|
||||||
}
|
}
|
||||||
@@ -1097,7 +1095,8 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
filters: {
|
filters: {
|
||||||
...sharedFilters,
|
...sharedFilters,
|
||||||
tagSlugs: tags,
|
tagSlugs: tags,
|
||||||
includeTagsInSearch: true
|
includeTagsInSearch: true,
|
||||||
|
includeMetadataInSearch: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
req.permission
|
req.permission
|
||||||
|
@@ -42,6 +42,7 @@ import { registerProjectEnvRouter } from "./project-env-router";
|
|||||||
import { registerProjectKeyRouter } from "./project-key-router";
|
import { registerProjectKeyRouter } from "./project-key-router";
|
||||||
import { registerProjectMembershipRouter } from "./project-membership-router";
|
import { registerProjectMembershipRouter } from "./project-membership-router";
|
||||||
import { registerProjectRouter } from "./project-router";
|
import { registerProjectRouter } from "./project-router";
|
||||||
|
import { SECRET_REMINDER_REGISTER_ROUTER_MAP } from "./reminder-routers";
|
||||||
import { registerSecretFolderRouter } from "./secret-folder-router";
|
import { registerSecretFolderRouter } from "./secret-folder-router";
|
||||||
import { registerSecretImportRouter } from "./secret-import-router";
|
import { registerSecretImportRouter } from "./secret-import-router";
|
||||||
import { registerSecretRequestsRouter } from "./secret-requests-router";
|
import { registerSecretRequestsRouter } from "./secret-requests-router";
|
||||||
@@ -172,4 +173,14 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
{ prefix: "/secret-syncs" }
|
{ prefix: "/secret-syncs" }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await server.register(
|
||||||
|
async (reminderRouter) => {
|
||||||
|
// register service specific reminder endpoints (reminders/secret)
|
||||||
|
for await (const [reminderType, router] of Object.entries(SECRET_REMINDER_REGISTER_ROUTER_MAP)) {
|
||||||
|
await reminderRouter.register(router, { prefix: `/${reminderType}` });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ prefix: "/reminders" }
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@@ -158,7 +158,8 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
includeRoles: z
|
includeRoles: z
|
||||||
.enum(["true", "false"])
|
.enum(["true", "false"])
|
||||||
.default("false")
|
.default("false")
|
||||||
.transform((value) => value === "true")
|
.transform((value) => value === "true"),
|
||||||
|
type: z.nativeEnum(ProjectType).optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -177,7 +178,8 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorOrgId: req.permission.orgId
|
actorOrgId: req.permission.orgId,
|
||||||
|
type: req.query.type
|
||||||
});
|
});
|
||||||
return { workspaces };
|
return { workspaces };
|
||||||
}
|
}
|
||||||
@@ -1050,6 +1052,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
body: z.object({
|
body: z.object({
|
||||||
limit: z.number().default(100),
|
limit: z.number().default(100),
|
||||||
offset: z.number().default(0),
|
offset: z.number().default(0),
|
||||||
|
type: z.nativeEnum(ProjectType).optional(),
|
||||||
orderBy: z.nativeEnum(SearchProjectSortBy).optional().default(SearchProjectSortBy.NAME),
|
orderBy: z.nativeEnum(SearchProjectSortBy).optional().default(SearchProjectSortBy.NAME),
|
||||||
orderDirection: z.nativeEnum(SortDirection).optional().default(SortDirection.ASC),
|
orderDirection: z.nativeEnum(SortDirection).optional().default(SortDirection.ASC),
|
||||||
name: z
|
name: z
|
||||||
|
8
backend/src/server/routes/v1/reminder-routers/index.ts
Normal file
8
backend/src/server/routes/v1/reminder-routers/index.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { ReminderType } from "@app/services/reminder/reminder-enums";
|
||||||
|
|
||||||
|
import { registerSecretReminderRouter } from "./secret-reminder-router";
|
||||||
|
|
||||||
|
export const SECRET_REMINDER_REGISTER_ROUTER_MAP: Record<ReminderType, (server: FastifyZodProvider) => Promise<void>> =
|
||||||
|
{
|
||||||
|
[ReminderType.SECRETS]: registerSecretReminderRouter
|
||||||
|
};
|
@@ -0,0 +1,154 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { RemindersSchema } from "@app/db/schemas/reminders";
|
||||||
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const registerSecretReminderRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
url: "/:secretId",
|
||||||
|
method: "POST",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
secretId: z.string().uuid()
|
||||||
|
}),
|
||||||
|
body: z
|
||||||
|
.object({
|
||||||
|
message: z.string().trim().max(1024).optional(),
|
||||||
|
repeatDays: z.number().min(1).nullable().optional(),
|
||||||
|
nextReminderDate: z.string().datetime().nullable().optional(),
|
||||||
|
recipients: z.string().array().optional()
|
||||||
|
})
|
||||||
|
.refine((data) => {
|
||||||
|
return data.repeatDays || data.nextReminderDate;
|
||||||
|
}, "At least one of repeatDays or nextReminderDate is required"),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
message: z.string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
await server.services.reminder.createReminder({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
reminder: {
|
||||||
|
secretId: req.params.secretId,
|
||||||
|
message: req.body.message,
|
||||||
|
repeatDays: req.body.repeatDays,
|
||||||
|
nextReminderDate: req.body.nextReminderDate,
|
||||||
|
recipients: req.body.recipients
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.CREATE_SECRET_REMINDER,
|
||||||
|
metadata: {
|
||||||
|
secretId: req.params.secretId,
|
||||||
|
message: req.body.message,
|
||||||
|
repeatDays: req.body.repeatDays,
|
||||||
|
nextReminderDate: req.body.nextReminderDate,
|
||||||
|
recipients: req.body.recipients
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { message: "Successfully created reminder" };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/:secretId",
|
||||||
|
method: "GET",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
secretId: z.string().uuid()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
reminder: RemindersSchema.extend({
|
||||||
|
recipients: z.string().array().optional()
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
.nullable()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const reminder = await server.services.reminder.getReminder({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
secretId: req.params.secretId
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.GET_SECRET_REMINDER,
|
||||||
|
metadata: {
|
||||||
|
secretId: req.params.secretId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { reminder };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/:secretId",
|
||||||
|
method: "DELETE",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
secretId: z.string().uuid()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
message: z.string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
await server.services.reminder.deleteReminder({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
secretId: req.params.secretId
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.DELETE_SECRET_REMINDER,
|
||||||
|
metadata: {
|
||||||
|
secretId: req.params.secretId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { message: "Successfully deleted reminder" };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@@ -0,0 +1,17 @@
|
|||||||
|
import {
|
||||||
|
BitbucketSyncSchema,
|
||||||
|
CreateBitbucketSyncSchema,
|
||||||
|
UpdateBitbucketSyncSchema
|
||||||
|
} from "@app/services/secret-sync/bitbucket";
|
||||||
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
|
||||||
|
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
|
||||||
|
|
||||||
|
export const registerBitbucketSyncRouter = async (server: FastifyZodProvider) =>
|
||||||
|
registerSyncSecretsEndpoints({
|
||||||
|
destination: SecretSync.Bitbucket,
|
||||||
|
server,
|
||||||
|
responseSchema: BitbucketSyncSchema,
|
||||||
|
createSchema: CreateBitbucketSyncSchema,
|
||||||
|
updateSchema: UpdateBitbucketSyncSchema
|
||||||
|
});
|
@@ -0,0 +1,17 @@
|
|||||||
|
import {
|
||||||
|
CreateDigitalOceanAppPlatformSyncSchema,
|
||||||
|
DigitalOceanAppPlatformSyncSchema,
|
||||||
|
UpdateDigitalOceanAppPlatformSyncSchema
|
||||||
|
} from "@app/services/secret-sync/digital-ocean-app-platform";
|
||||||
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
|
||||||
|
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
|
||||||
|
|
||||||
|
export const registerDigitalOceanAppPlatformSyncRouter = async (server: FastifyZodProvider) =>
|
||||||
|
registerSyncSecretsEndpoints({
|
||||||
|
destination: SecretSync.DigitalOceanAppPlatform,
|
||||||
|
server,
|
||||||
|
responseSchema: DigitalOceanAppPlatformSyncSchema,
|
||||||
|
createSchema: CreateDigitalOceanAppPlatformSyncSchema,
|
||||||
|
updateSchema: UpdateDigitalOceanAppPlatformSyncSchema
|
||||||
|
});
|
@@ -7,11 +7,13 @@ import { registerAwsSecretsManagerSyncRouter } from "./aws-secrets-manager-sync-
|
|||||||
import { registerAzureAppConfigurationSyncRouter } from "./azure-app-configuration-sync-router";
|
import { registerAzureAppConfigurationSyncRouter } from "./azure-app-configuration-sync-router";
|
||||||
import { registerAzureDevOpsSyncRouter } from "./azure-devops-sync-router";
|
import { registerAzureDevOpsSyncRouter } from "./azure-devops-sync-router";
|
||||||
import { registerAzureKeyVaultSyncRouter } from "./azure-key-vault-sync-router";
|
import { registerAzureKeyVaultSyncRouter } from "./azure-key-vault-sync-router";
|
||||||
|
import { registerBitbucketSyncRouter } from "./bitbucket-sync-router";
|
||||||
import { registerCamundaSyncRouter } from "./camunda-sync-router";
|
import { registerCamundaSyncRouter } from "./camunda-sync-router";
|
||||||
import { registerChecklySyncRouter } from "./checkly-sync-router";
|
import { registerChecklySyncRouter } from "./checkly-sync-router";
|
||||||
import { registerCloudflarePagesSyncRouter } from "./cloudflare-pages-sync-router";
|
import { registerCloudflarePagesSyncRouter } from "./cloudflare-pages-sync-router";
|
||||||
import { registerCloudflareWorkersSyncRouter } from "./cloudflare-workers-sync-router";
|
import { registerCloudflareWorkersSyncRouter } from "./cloudflare-workers-sync-router";
|
||||||
import { registerDatabricksSyncRouter } from "./databricks-sync-router";
|
import { registerDatabricksSyncRouter } from "./databricks-sync-router";
|
||||||
|
import { registerDigitalOceanAppPlatformSyncRouter } from "./digital-ocean-app-platform-sync-router";
|
||||||
import { registerFlyioSyncRouter } from "./flyio-sync-router";
|
import { registerFlyioSyncRouter } from "./flyio-sync-router";
|
||||||
import { registerGcpSyncRouter } from "./gcp-sync-router";
|
import { registerGcpSyncRouter } from "./gcp-sync-router";
|
||||||
import { registerGitHubSyncRouter } from "./github-sync-router";
|
import { registerGitHubSyncRouter } from "./github-sync-router";
|
||||||
@@ -57,5 +59,7 @@ export const SECRET_SYNC_REGISTER_ROUTER_MAP: Record<SecretSync, (server: Fastif
|
|||||||
[SecretSync.Supabase]: registerSupabaseSyncRouter,
|
[SecretSync.Supabase]: registerSupabaseSyncRouter,
|
||||||
[SecretSync.Zabbix]: registerZabbixSyncRouter,
|
[SecretSync.Zabbix]: registerZabbixSyncRouter,
|
||||||
[SecretSync.Railway]: registerRailwaySyncRouter,
|
[SecretSync.Railway]: registerRailwaySyncRouter,
|
||||||
[SecretSync.Checkly]: registerChecklySyncRouter
|
[SecretSync.Checkly]: registerChecklySyncRouter,
|
||||||
|
[SecretSync.DigitalOceanAppPlatform]: registerDigitalOceanAppPlatformSyncRouter,
|
||||||
|
[SecretSync.Bitbucket]: registerBitbucketSyncRouter
|
||||||
};
|
};
|
||||||
|
@@ -21,6 +21,7 @@ import {
|
|||||||
} from "@app/services/secret-sync/azure-app-configuration";
|
} from "@app/services/secret-sync/azure-app-configuration";
|
||||||
import { AzureDevOpsSyncListItemSchema, AzureDevOpsSyncSchema } from "@app/services/secret-sync/azure-devops";
|
import { AzureDevOpsSyncListItemSchema, AzureDevOpsSyncSchema } from "@app/services/secret-sync/azure-devops";
|
||||||
import { AzureKeyVaultSyncListItemSchema, AzureKeyVaultSyncSchema } from "@app/services/secret-sync/azure-key-vault";
|
import { AzureKeyVaultSyncListItemSchema, AzureKeyVaultSyncSchema } from "@app/services/secret-sync/azure-key-vault";
|
||||||
|
import { BitbucketSyncListItemSchema, BitbucketSyncSchema } from "@app/services/secret-sync/bitbucket";
|
||||||
import { CamundaSyncListItemSchema, CamundaSyncSchema } from "@app/services/secret-sync/camunda";
|
import { CamundaSyncListItemSchema, CamundaSyncSchema } from "@app/services/secret-sync/camunda";
|
||||||
import { ChecklySyncListItemSchema, ChecklySyncSchema } from "@app/services/secret-sync/checkly/checkly-sync-schemas";
|
import { ChecklySyncListItemSchema, ChecklySyncSchema } from "@app/services/secret-sync/checkly/checkly-sync-schemas";
|
||||||
import {
|
import {
|
||||||
@@ -32,6 +33,10 @@ import {
|
|||||||
CloudflareWorkersSyncSchema
|
CloudflareWorkersSyncSchema
|
||||||
} from "@app/services/secret-sync/cloudflare-workers/cloudflare-workers-schemas";
|
} from "@app/services/secret-sync/cloudflare-workers/cloudflare-workers-schemas";
|
||||||
import { DatabricksSyncListItemSchema, DatabricksSyncSchema } from "@app/services/secret-sync/databricks";
|
import { DatabricksSyncListItemSchema, DatabricksSyncSchema } from "@app/services/secret-sync/databricks";
|
||||||
|
import {
|
||||||
|
DigitalOceanAppPlatformSyncListItemSchema,
|
||||||
|
DigitalOceanAppPlatformSyncSchema
|
||||||
|
} from "@app/services/secret-sync/digital-ocean-app-platform";
|
||||||
import { FlyioSyncListItemSchema, FlyioSyncSchema } from "@app/services/secret-sync/flyio";
|
import { FlyioSyncListItemSchema, FlyioSyncSchema } from "@app/services/secret-sync/flyio";
|
||||||
import { GcpSyncListItemSchema, GcpSyncSchema } from "@app/services/secret-sync/gcp";
|
import { GcpSyncListItemSchema, GcpSyncSchema } from "@app/services/secret-sync/gcp";
|
||||||
import { GitHubSyncListItemSchema, GitHubSyncSchema } from "@app/services/secret-sync/github";
|
import { GitHubSyncListItemSchema, GitHubSyncSchema } from "@app/services/secret-sync/github";
|
||||||
@@ -75,7 +80,9 @@ const SecretSyncSchema = z.discriminatedUnion("destination", [
|
|||||||
SupabaseSyncSchema,
|
SupabaseSyncSchema,
|
||||||
ZabbixSyncSchema,
|
ZabbixSyncSchema,
|
||||||
RailwaySyncSchema,
|
RailwaySyncSchema,
|
||||||
ChecklySyncSchema
|
ChecklySyncSchema,
|
||||||
|
DigitalOceanAppPlatformSyncSchema,
|
||||||
|
BitbucketSyncSchema
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
||||||
@@ -102,11 +109,12 @@ const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
|||||||
GitLabSyncListItemSchema,
|
GitLabSyncListItemSchema,
|
||||||
CloudflarePagesSyncListItemSchema,
|
CloudflarePagesSyncListItemSchema,
|
||||||
CloudflareWorkersSyncListItemSchema,
|
CloudflareWorkersSyncListItemSchema,
|
||||||
|
DigitalOceanAppPlatformSyncListItemSchema,
|
||||||
ZabbixSyncListItemSchema,
|
ZabbixSyncListItemSchema,
|
||||||
RailwaySyncListItemSchema,
|
RailwaySyncListItemSchema,
|
||||||
ChecklySyncListItemSchema,
|
ChecklySyncListItemSchema,
|
||||||
SupabaseSyncListItemSchema
|
SupabaseSyncListItemSchema,
|
||||||
|
BitbucketSyncListItemSchema
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const registerSecretSyncRouter = async (server: FastifyZodProvider) => {
|
export const registerSecretSyncRouter = async (server: FastifyZodProvider) => {
|
||||||
|
@@ -1,9 +1,11 @@
|
|||||||
import fastifyMultipart from "@fastify/multipart";
|
import fastifyMultipart from "@fastify/multipart";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { readLimit } from "@app/server/config/rateLimiter";
|
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";
|
||||||
|
import { VaultMappingType } from "@app/services/external-migration/external-migration-types";
|
||||||
|
|
||||||
const MB25_IN_BYTES = 26214400;
|
const MB25_IN_BYTES = 26214400;
|
||||||
|
|
||||||
@@ -15,7 +17,7 @@ export const registerExternalMigrationRouter = async (server: FastifyZodProvider
|
|||||||
bodyLimit: MB25_IN_BYTES,
|
bodyLimit: MB25_IN_BYTES,
|
||||||
url: "/env-key",
|
url: "/env-key",
|
||||||
config: {
|
config: {
|
||||||
rateLimit: readLimit
|
rateLimit: writeLimit
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
@@ -52,4 +54,30 @@ export const registerExternalMigrationRouter = async (server: FastifyZodProvider
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/vault",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
vaultAccessToken: z.string(),
|
||||||
|
vaultNamespace: z.string().trim().optional(),
|
||||||
|
vaultUrl: z.string(),
|
||||||
|
mappingType: z.nativeEnum(VaultMappingType)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
await server.services.migration.importVaultData({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@@ -11,5 +11,5 @@ export const registerV3Routes = async (server: FastifyZodProvider) => {
|
|||||||
await server.register(registerUserRouter, { prefix: "/users" });
|
await server.register(registerUserRouter, { prefix: "/users" });
|
||||||
await server.register(registerSecretRouter, { prefix: "/secrets" });
|
await server.register(registerSecretRouter, { prefix: "/secrets" });
|
||||||
await server.register(registerSecretBlindIndexRouter, { prefix: "/workspaces" });
|
await server.register(registerSecretBlindIndexRouter, { prefix: "/workspaces" });
|
||||||
await server.register(registerExternalMigrationRouter, { prefix: "/migrate" });
|
await server.register(registerExternalMigrationRouter, { prefix: "/external-migration" });
|
||||||
};
|
};
|
||||||
|
@@ -33,6 +33,7 @@ export enum AppConnection {
|
|||||||
Bitbucket = "bitbucket",
|
Bitbucket = "bitbucket",
|
||||||
Checkly = "checkly",
|
Checkly = "checkly",
|
||||||
Supabase = "supabase",
|
Supabase = "supabase",
|
||||||
|
DigitalOcean = "digital-ocean",
|
||||||
Okta = "okta"
|
Okta = "okta"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -68,6 +68,11 @@ import {
|
|||||||
getDatabricksConnectionListItem,
|
getDatabricksConnectionListItem,
|
||||||
validateDatabricksConnectionCredentials
|
validateDatabricksConnectionCredentials
|
||||||
} from "./databricks";
|
} from "./databricks";
|
||||||
|
import {
|
||||||
|
DigitalOceanConnectionMethod,
|
||||||
|
getDigitalOceanConnectionListItem,
|
||||||
|
validateDigitalOceanConnectionCredentials
|
||||||
|
} from "./digital-ocean";
|
||||||
import { FlyioConnectionMethod, getFlyioConnectionListItem, validateFlyioConnectionCredentials } from "./flyio";
|
import { FlyioConnectionMethod, getFlyioConnectionListItem, validateFlyioConnectionCredentials } from "./flyio";
|
||||||
import { GcpConnectionMethod, getGcpConnectionListItem, validateGcpConnectionCredentials } from "./gcp";
|
import { GcpConnectionMethod, getGcpConnectionListItem, validateGcpConnectionCredentials } from "./gcp";
|
||||||
import { getGitHubConnectionListItem, GitHubConnectionMethod, validateGitHubConnectionCredentials } from "./github";
|
import { getGitHubConnectionListItem, GitHubConnectionMethod, validateGitHubConnectionCredentials } from "./github";
|
||||||
@@ -157,6 +162,7 @@ export const listAppConnectionOptions = () => {
|
|||||||
getBitbucketConnectionListItem(),
|
getBitbucketConnectionListItem(),
|
||||||
getChecklyConnectionListItem(),
|
getChecklyConnectionListItem(),
|
||||||
getSupabaseConnectionListItem(),
|
getSupabaseConnectionListItem(),
|
||||||
|
getDigitalOceanConnectionListItem(),
|
||||||
getOktaConnectionListItem()
|
getOktaConnectionListItem()
|
||||||
].sort((a, b) => a.name.localeCompare(b.name));
|
].sort((a, b) => a.name.localeCompare(b.name));
|
||||||
};
|
};
|
||||||
@@ -244,6 +250,7 @@ export const validateAppConnectionCredentials = async (
|
|||||||
[AppConnection.Bitbucket]: validateBitbucketConnectionCredentials as TAppConnectionCredentialsValidator,
|
[AppConnection.Bitbucket]: validateBitbucketConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||||
[AppConnection.Checkly]: validateChecklyConnectionCredentials as TAppConnectionCredentialsValidator,
|
[AppConnection.Checkly]: validateChecklyConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||||
[AppConnection.Supabase]: validateSupabaseConnectionCredentials as TAppConnectionCredentialsValidator,
|
[AppConnection.Supabase]: validateSupabaseConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||||
|
[AppConnection.DigitalOcean]: validateDigitalOceanConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||||
[AppConnection.Okta]: validateOktaConnectionCredentials as TAppConnectionCredentialsValidator
|
[AppConnection.Okta]: validateOktaConnectionCredentials as TAppConnectionCredentialsValidator
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -283,6 +290,7 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
|
|||||||
case CloudflareConnectionMethod.APIToken:
|
case CloudflareConnectionMethod.APIToken:
|
||||||
case BitbucketConnectionMethod.ApiToken:
|
case BitbucketConnectionMethod.ApiToken:
|
||||||
case ZabbixConnectionMethod.ApiToken:
|
case ZabbixConnectionMethod.ApiToken:
|
||||||
|
case DigitalOceanConnectionMethod.ApiToken:
|
||||||
case OktaConnectionMethod.ApiToken:
|
case OktaConnectionMethod.ApiToken:
|
||||||
return "API Token";
|
return "API Token";
|
||||||
case PostgresConnectionMethod.UsernameAndPassword:
|
case PostgresConnectionMethod.UsernameAndPassword:
|
||||||
@@ -372,6 +380,7 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record<
|
|||||||
[AppConnection.Bitbucket]: platformManagedCredentialsNotSupported,
|
[AppConnection.Bitbucket]: platformManagedCredentialsNotSupported,
|
||||||
[AppConnection.Checkly]: platformManagedCredentialsNotSupported,
|
[AppConnection.Checkly]: platformManagedCredentialsNotSupported,
|
||||||
[AppConnection.Supabase]: platformManagedCredentialsNotSupported,
|
[AppConnection.Supabase]: platformManagedCredentialsNotSupported,
|
||||||
|
[AppConnection.DigitalOcean]: platformManagedCredentialsNotSupported,
|
||||||
[AppConnection.Okta]: platformManagedCredentialsNotSupported
|
[AppConnection.Okta]: platformManagedCredentialsNotSupported
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -35,6 +35,7 @@ export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
|
|||||||
[AppConnection.Bitbucket]: "Bitbucket",
|
[AppConnection.Bitbucket]: "Bitbucket",
|
||||||
[AppConnection.Checkly]: "Checkly",
|
[AppConnection.Checkly]: "Checkly",
|
||||||
[AppConnection.Supabase]: "Supabase",
|
[AppConnection.Supabase]: "Supabase",
|
||||||
|
[AppConnection.DigitalOcean]: "DigitalOcean App Platform",
|
||||||
[AppConnection.Okta]: "Okta"
|
[AppConnection.Okta]: "Okta"
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -73,5 +74,6 @@ export const APP_CONNECTION_PLAN_MAP: Record<AppConnection, AppConnectionPlanTyp
|
|||||||
[AppConnection.Bitbucket]: AppConnectionPlanType.Regular,
|
[AppConnection.Bitbucket]: AppConnectionPlanType.Regular,
|
||||||
[AppConnection.Checkly]: AppConnectionPlanType.Regular,
|
[AppConnection.Checkly]: AppConnectionPlanType.Regular,
|
||||||
[AppConnection.Supabase]: AppConnectionPlanType.Regular,
|
[AppConnection.Supabase]: AppConnectionPlanType.Regular,
|
||||||
|
[AppConnection.DigitalOcean]: AppConnectionPlanType.Regular,
|
||||||
[AppConnection.Okta]: AppConnectionPlanType.Regular
|
[AppConnection.Okta]: AppConnectionPlanType.Regular
|
||||||
};
|
};
|
||||||
|
@@ -61,6 +61,8 @@ import { ValidateCloudflareConnectionCredentialsSchema } from "./cloudflare/clou
|
|||||||
import { cloudflareConnectionService } from "./cloudflare/cloudflare-connection-service";
|
import { cloudflareConnectionService } from "./cloudflare/cloudflare-connection-service";
|
||||||
import { ValidateDatabricksConnectionCredentialsSchema } from "./databricks";
|
import { ValidateDatabricksConnectionCredentialsSchema } from "./databricks";
|
||||||
import { databricksConnectionService } from "./databricks/databricks-connection-service";
|
import { databricksConnectionService } from "./databricks/databricks-connection-service";
|
||||||
|
import { ValidateDigitalOceanConnectionCredentialsSchema } from "./digital-ocean";
|
||||||
|
import { digitalOceanAppPlatformConnectionService } from "./digital-ocean/digital-ocean-connection-service";
|
||||||
import { ValidateFlyioConnectionCredentialsSchema } from "./flyio";
|
import { ValidateFlyioConnectionCredentialsSchema } from "./flyio";
|
||||||
import { flyioConnectionService } from "./flyio/flyio-connection-service";
|
import { flyioConnectionService } from "./flyio/flyio-connection-service";
|
||||||
import { ValidateGcpConnectionCredentialsSchema } from "./gcp";
|
import { ValidateGcpConnectionCredentialsSchema } from "./gcp";
|
||||||
@@ -145,6 +147,7 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
|
|||||||
[AppConnection.Bitbucket]: ValidateBitbucketConnectionCredentialsSchema,
|
[AppConnection.Bitbucket]: ValidateBitbucketConnectionCredentialsSchema,
|
||||||
[AppConnection.Checkly]: ValidateChecklyConnectionCredentialsSchema,
|
[AppConnection.Checkly]: ValidateChecklyConnectionCredentialsSchema,
|
||||||
[AppConnection.Supabase]: ValidateSupabaseConnectionCredentialsSchema,
|
[AppConnection.Supabase]: ValidateSupabaseConnectionCredentialsSchema,
|
||||||
|
[AppConnection.DigitalOcean]: ValidateDigitalOceanConnectionCredentialsSchema,
|
||||||
[AppConnection.Okta]: ValidateOktaConnectionCredentialsSchema
|
[AppConnection.Okta]: ValidateOktaConnectionCredentialsSchema
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -607,6 +610,7 @@ export const appConnectionServiceFactory = ({
|
|||||||
bitbucket: bitbucketConnectionService(connectAppConnectionById),
|
bitbucket: bitbucketConnectionService(connectAppConnectionById),
|
||||||
checkly: checklyConnectionService(connectAppConnectionById),
|
checkly: checklyConnectionService(connectAppConnectionById),
|
||||||
supabase: supabaseConnectionService(connectAppConnectionById),
|
supabase: supabaseConnectionService(connectAppConnectionById),
|
||||||
|
digitalOcean: digitalOceanAppPlatformConnectionService(connectAppConnectionById),
|
||||||
okta: oktaConnectionService(connectAppConnectionById)
|
okta: oktaConnectionService(connectAppConnectionById)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -87,6 +87,12 @@ import {
|
|||||||
TDatabricksConnectionInput,
|
TDatabricksConnectionInput,
|
||||||
TValidateDatabricksConnectionCredentialsSchema
|
TValidateDatabricksConnectionCredentialsSchema
|
||||||
} from "./databricks";
|
} from "./databricks";
|
||||||
|
import {
|
||||||
|
TDigitalOceanConnection,
|
||||||
|
TDigitalOceanConnectionConfig,
|
||||||
|
TDigitalOceanConnectionInput,
|
||||||
|
TValidateDigitalOceanCredentialsSchema
|
||||||
|
} from "./digital-ocean";
|
||||||
import {
|
import {
|
||||||
TFlyioConnection,
|
TFlyioConnection,
|
||||||
TFlyioConnectionConfig,
|
TFlyioConnectionConfig,
|
||||||
@@ -238,6 +244,7 @@ export type TAppConnection = { id: string } & (
|
|||||||
| TRailwayConnection
|
| TRailwayConnection
|
||||||
| TChecklyConnection
|
| TChecklyConnection
|
||||||
| TSupabaseConnection
|
| TSupabaseConnection
|
||||||
|
| TDigitalOceanConnection
|
||||||
| TOktaConnection
|
| TOktaConnection
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -280,6 +287,7 @@ export type TAppConnectionInput = { id: string } & (
|
|||||||
| TRailwayConnectionInput
|
| TRailwayConnectionInput
|
||||||
| TChecklyConnectionInput
|
| TChecklyConnectionInput
|
||||||
| TSupabaseConnectionInput
|
| TSupabaseConnectionInput
|
||||||
|
| TDigitalOceanConnectionInput
|
||||||
| TOktaConnectionInput
|
| TOktaConnectionInput
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -330,6 +338,7 @@ export type TAppConnectionConfig =
|
|||||||
| TRailwayConnectionConfig
|
| TRailwayConnectionConfig
|
||||||
| TChecklyConnectionConfig
|
| TChecklyConnectionConfig
|
||||||
| TSupabaseConnectionConfig
|
| TSupabaseConnectionConfig
|
||||||
|
| TDigitalOceanConnectionConfig
|
||||||
| TOktaConnectionConfig;
|
| TOktaConnectionConfig;
|
||||||
|
|
||||||
export type TValidateAppConnectionCredentialsSchema =
|
export type TValidateAppConnectionCredentialsSchema =
|
||||||
@@ -367,6 +376,7 @@ export type TValidateAppConnectionCredentialsSchema =
|
|||||||
| TValidateRailwayConnectionCredentialsSchema
|
| TValidateRailwayConnectionCredentialsSchema
|
||||||
| TValidateChecklyConnectionCredentialsSchema
|
| TValidateChecklyConnectionCredentialsSchema
|
||||||
| TValidateSupabaseConnectionCredentialsSchema
|
| TValidateSupabaseConnectionCredentialsSchema
|
||||||
|
| TValidateDigitalOceanCredentialsSchema
|
||||||
| TValidateOktaConnectionCredentialsSchema;
|
| TValidateOktaConnectionCredentialsSchema;
|
||||||
|
|
||||||
export type TListAwsConnectionKmsKeys = {
|
export type TListAwsConnectionKmsKeys = {
|
||||||
|
@@ -14,13 +14,13 @@ import {
|
|||||||
} from "./azure-app-configuration-connection-types";
|
} from "./azure-app-configuration-connection-types";
|
||||||
|
|
||||||
export const getAzureAppConfigurationConnectionListItem = () => {
|
export const getAzureAppConfigurationConnectionListItem = () => {
|
||||||
const { INF_APP_CONNECTION_AZURE_CLIENT_ID } = getConfig();
|
const { INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_ID } = getConfig();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "Azure App Configuration" as const,
|
name: "Azure App Configuration" as const,
|
||||||
app: AppConnection.AzureAppConfiguration as const,
|
app: AppConnection.AzureAppConfiguration as const,
|
||||||
methods: Object.values(AzureAppConfigurationConnectionMethod) as [AzureAppConfigurationConnectionMethod.OAuth],
|
methods: Object.values(AzureAppConfigurationConnectionMethod) as [AzureAppConfigurationConnectionMethod.OAuth],
|
||||||
oauthClientId: INF_APP_CONNECTION_AZURE_CLIENT_ID
|
oauthClientId: INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_ID
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -29,9 +29,16 @@ export const validateAzureAppConfigurationConnectionCredentials = async (
|
|||||||
) => {
|
) => {
|
||||||
const { credentials: inputCredentials, method } = config;
|
const { credentials: inputCredentials, method } = config;
|
||||||
|
|
||||||
const { INF_APP_CONNECTION_AZURE_CLIENT_ID, INF_APP_CONNECTION_AZURE_CLIENT_SECRET, SITE_URL } = getConfig();
|
const {
|
||||||
|
INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_ID,
|
||||||
|
INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_SECRET,
|
||||||
|
SITE_URL
|
||||||
|
} = getConfig();
|
||||||
|
|
||||||
if (!INF_APP_CONNECTION_AZURE_CLIENT_ID || !INF_APP_CONNECTION_AZURE_CLIENT_SECRET) {
|
if (
|
||||||
|
!INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_ID ||
|
||||||
|
!INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_SECRET
|
||||||
|
) {
|
||||||
throw new InternalServerError({
|
throw new InternalServerError({
|
||||||
message: `Azure ${getAppConnectionMethodName(method)} environment variables have not been configured`
|
message: `Azure ${getAppConnectionMethodName(method)} environment variables have not been configured`
|
||||||
});
|
});
|
||||||
@@ -47,8 +54,8 @@ export const validateAzureAppConfigurationConnectionCredentials = async (
|
|||||||
grant_type: "authorization_code",
|
grant_type: "authorization_code",
|
||||||
code: inputCredentials.code,
|
code: inputCredentials.code,
|
||||||
scope: `openid offline_access https://azconfig.io/.default`,
|
scope: `openid offline_access https://azconfig.io/.default`,
|
||||||
client_id: INF_APP_CONNECTION_AZURE_CLIENT_ID,
|
client_id: INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_ID,
|
||||||
client_secret: INF_APP_CONNECTION_AZURE_CLIENT_SECRET,
|
client_secret: INF_APP_CONNECTION_AZURE_APP_CONFIGURATION_CLIENT_SECRET,
|
||||||
redirect_uri: `${SITE_URL}/organization/app-connections/azure/oauth/callback`
|
redirect_uri: `${SITE_URL}/organization/app-connections/azure/oauth/callback`
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
export enum AzureClientSecretsConnectionMethod {
|
export enum AzureClientSecretsConnectionMethod {
|
||||||
OAuth = "oauth"
|
OAuth = "oauth",
|
||||||
|
ClientSecret = "client-secret"
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-case-declarations */
|
||||||
import { AxiosError, AxiosResponse } from "axios";
|
import { AxiosError, AxiosResponse } from "axios";
|
||||||
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
@@ -16,18 +17,22 @@ import { AppConnection } from "../app-connection-enums";
|
|||||||
import { AzureClientSecretsConnectionMethod } from "./azure-client-secrets-connection-enums";
|
import { AzureClientSecretsConnectionMethod } from "./azure-client-secrets-connection-enums";
|
||||||
import {
|
import {
|
||||||
ExchangeCodeAzureResponse,
|
ExchangeCodeAzureResponse,
|
||||||
|
TAzureClientSecretsConnectionClientSecretCredentials,
|
||||||
TAzureClientSecretsConnectionConfig,
|
TAzureClientSecretsConnectionConfig,
|
||||||
TAzureClientSecretsConnectionCredentials
|
TAzureClientSecretsConnectionCredentials
|
||||||
} from "./azure-client-secrets-connection-types";
|
} from "./azure-client-secrets-connection-types";
|
||||||
|
|
||||||
export const getAzureClientSecretsConnectionListItem = () => {
|
export const getAzureClientSecretsConnectionListItem = () => {
|
||||||
const { INF_APP_CONNECTION_AZURE_CLIENT_ID } = getConfig();
|
const { INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_ID } = getConfig();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "Azure Client Secrets" as const,
|
name: "Azure Client Secrets" as const,
|
||||||
app: AppConnection.AzureClientSecrets as const,
|
app: AppConnection.AzureClientSecrets as const,
|
||||||
methods: Object.values(AzureClientSecretsConnectionMethod) as [AzureClientSecretsConnectionMethod.OAuth],
|
methods: Object.values(AzureClientSecretsConnectionMethod) as [
|
||||||
oauthClientId: INF_APP_CONNECTION_AZURE_CLIENT_ID
|
AzureClientSecretsConnectionMethod.OAuth,
|
||||||
|
AzureClientSecretsConnectionMethod.ClientSecret
|
||||||
|
],
|
||||||
|
oauthClientId: INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_ID
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -37,12 +42,6 @@ export const getAzureConnectionAccessToken = async (
|
|||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||||
) => {
|
) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
if (!appCfg.INF_APP_CONNECTION_AZURE_CLIENT_ID || !appCfg.INF_APP_CONNECTION_AZURE_CLIENT_SECRET) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: `Azure environment variables have not been configured`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const appConnection = await appConnectionDAL.findById(connectionId);
|
const appConnection = await appConnectionDAL.findById(connectionId);
|
||||||
|
|
||||||
if (!appConnection) {
|
if (!appConnection) {
|
||||||
@@ -63,104 +62,195 @@ export const getAzureConnectionAccessToken = async (
|
|||||||
|
|
||||||
const { refreshToken } = credentials;
|
const { refreshToken } = credentials;
|
||||||
const currentTime = Date.now();
|
const currentTime = Date.now();
|
||||||
|
switch (appConnection.method) {
|
||||||
|
case AzureClientSecretsConnectionMethod.OAuth:
|
||||||
|
if (
|
||||||
|
!appCfg.INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_ID ||
|
||||||
|
!appCfg.INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_SECRET
|
||||||
|
) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Azure OAuth environment variables have not been configured`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const { data } = await request.post<ExchangeCodeAzureResponse>(
|
||||||
|
IntegrationUrls.AZURE_TOKEN_URL.replace("common", credentials.tenantId || "common"),
|
||||||
|
new URLSearchParams({
|
||||||
|
grant_type: "refresh_token",
|
||||||
|
scope: `openid offline_access https://graph.microsoft.com/.default`,
|
||||||
|
client_id: appCfg.INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_ID,
|
||||||
|
client_secret: appCfg.INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_SECRET,
|
||||||
|
refresh_token: refreshToken
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const { data } = await request.post<ExchangeCodeAzureResponse>(
|
const updatedCredentials = {
|
||||||
IntegrationUrls.AZURE_TOKEN_URL.replace("common", credentials.tenantId || "common"),
|
...credentials,
|
||||||
new URLSearchParams({
|
accessToken: data.access_token,
|
||||||
grant_type: "refresh_token",
|
expiresAt: currentTime + data.expires_in * 1000,
|
||||||
scope: `openid offline_access https://graph.microsoft.com/.default`,
|
refreshToken: data.refresh_token
|
||||||
client_id: appCfg.INF_APP_CONNECTION_AZURE_CLIENT_ID,
|
};
|
||||||
client_secret: appCfg.INF_APP_CONNECTION_AZURE_CLIENT_SECRET,
|
|
||||||
refresh_token: refreshToken
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const updatedCredentials = {
|
const encryptedCredentials = await encryptAppConnectionCredentials({
|
||||||
...credentials,
|
credentials: updatedCredentials,
|
||||||
accessToken: data.access_token,
|
orgId: appConnection.orgId,
|
||||||
expiresAt: currentTime + data.expires_in * 1000,
|
kmsService
|
||||||
refreshToken: data.refresh_token
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const encryptedCredentials = await encryptAppConnectionCredentials({
|
await appConnectionDAL.updateById(appConnection.id, { encryptedCredentials });
|
||||||
credentials: updatedCredentials,
|
|
||||||
orgId: appConnection.orgId,
|
|
||||||
kmsService
|
|
||||||
});
|
|
||||||
|
|
||||||
await appConnectionDAL.updateById(appConnection.id, { encryptedCredentials });
|
return data.access_token;
|
||||||
|
case AzureClientSecretsConnectionMethod.ClientSecret:
|
||||||
|
const accessTokenCredentials = (await decryptAppConnectionCredentials({
|
||||||
|
orgId: appConnection.orgId,
|
||||||
|
kmsService,
|
||||||
|
encryptedCredentials: appConnection.encryptedCredentials
|
||||||
|
})) as TAzureClientSecretsConnectionClientSecretCredentials;
|
||||||
|
const { accessToken, expiresAt, clientId, clientSecret, tenantId } = accessTokenCredentials;
|
||||||
|
if (accessToken && expiresAt && expiresAt > currentTime + 300000) {
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
return data.access_token;
|
const { data: clientData } = await request.post<ExchangeCodeAzureResponse>(
|
||||||
|
IntegrationUrls.AZURE_TOKEN_URL.replace("common", tenantId || "common"),
|
||||||
|
new URLSearchParams({
|
||||||
|
grant_type: "client_credentials",
|
||||||
|
scope: `https://graph.microsoft.com/.default`,
|
||||||
|
client_id: clientId,
|
||||||
|
client_secret: clientSecret
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const updatedClientCredentials = {
|
||||||
|
...accessTokenCredentials,
|
||||||
|
accessToken: clientData.access_token,
|
||||||
|
expiresAt: currentTime + clientData.expires_in * 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
const encryptedClientCredentials = await encryptAppConnectionCredentials({
|
||||||
|
credentials: updatedClientCredentials,
|
||||||
|
orgId: appConnection.orgId,
|
||||||
|
kmsService
|
||||||
|
});
|
||||||
|
|
||||||
|
await appConnectionDAL.updateById(appConnection.id, { encryptedCredentials: encryptedClientCredentials });
|
||||||
|
|
||||||
|
return clientData.access_token;
|
||||||
|
default:
|
||||||
|
throw new InternalServerError({
|
||||||
|
message: `Unhandled Azure connection method: ${appConnection.method as AzureClientSecretsConnectionMethod}`
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const validateAzureClientSecretsConnectionCredentials = async (config: TAzureClientSecretsConnectionConfig) => {
|
export const validateAzureClientSecretsConnectionCredentials = async (config: TAzureClientSecretsConnectionConfig) => {
|
||||||
const { credentials: inputCredentials, method } = config;
|
const { credentials: inputCredentials, method } = config;
|
||||||
|
|
||||||
const { INF_APP_CONNECTION_AZURE_CLIENT_ID, INF_APP_CONNECTION_AZURE_CLIENT_SECRET, SITE_URL } = getConfig();
|
const {
|
||||||
|
INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_ID,
|
||||||
if (!SITE_URL) {
|
INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_SECRET,
|
||||||
throw new InternalServerError({ message: "SITE_URL env var is required to complete Azure OAuth flow" });
|
SITE_URL
|
||||||
}
|
} = getConfig();
|
||||||
|
|
||||||
if (!INF_APP_CONNECTION_AZURE_CLIENT_ID || !INF_APP_CONNECTION_AZURE_CLIENT_SECRET) {
|
|
||||||
throw new InternalServerError({
|
|
||||||
message: `Azure ${getAppConnectionMethodName(method)} environment variables have not been configured`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let tokenResp: AxiosResponse<ExchangeCodeAzureResponse> | null = null;
|
|
||||||
let tokenError: AxiosError | null = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
tokenResp = await request.post<ExchangeCodeAzureResponse>(
|
|
||||||
IntegrationUrls.AZURE_TOKEN_URL.replace("common", inputCredentials.tenantId || "common"),
|
|
||||||
new URLSearchParams({
|
|
||||||
grant_type: "authorization_code",
|
|
||||||
code: inputCredentials.code,
|
|
||||||
scope: `openid offline_access https://graph.microsoft.com/.default`,
|
|
||||||
client_id: INF_APP_CONNECTION_AZURE_CLIENT_ID,
|
|
||||||
client_secret: INF_APP_CONNECTION_AZURE_CLIENT_SECRET,
|
|
||||||
redirect_uri: `${SITE_URL}/organization/app-connections/azure/oauth/callback`
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} catch (e: unknown) {
|
|
||||||
if (e instanceof AxiosError) {
|
|
||||||
tokenError = e;
|
|
||||||
} else {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: `Unable to validate connection: verify credentials`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tokenError) {
|
|
||||||
if (tokenError instanceof AxiosError) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: `Failed to get access token: ${
|
|
||||||
(tokenError?.response?.data as { error_description?: string })?.error_description || "Unknown error"
|
|
||||||
}`
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new InternalServerError({
|
|
||||||
message: "Failed to get access token"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tokenResp) {
|
|
||||||
throw new InternalServerError({
|
|
||||||
message: `Failed to get access token: Token was empty with no error`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case AzureClientSecretsConnectionMethod.OAuth:
|
case AzureClientSecretsConnectionMethod.OAuth:
|
||||||
|
if (!SITE_URL) {
|
||||||
|
throw new InternalServerError({ message: "SITE_URL env var is required to complete Azure OAuth flow" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_ID ||
|
||||||
|
!INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_SECRET
|
||||||
|
) {
|
||||||
|
throw new InternalServerError({
|
||||||
|
message: `Azure ${getAppConnectionMethodName(method)} environment variables have not been configured`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokenResp: AxiosResponse<ExchangeCodeAzureResponse> | null = null;
|
||||||
|
let tokenError: AxiosError | null = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
tokenResp = await request.post<ExchangeCodeAzureResponse>(
|
||||||
|
IntegrationUrls.AZURE_TOKEN_URL.replace("common", inputCredentials.tenantId || "common"),
|
||||||
|
new URLSearchParams({
|
||||||
|
grant_type: "authorization_code",
|
||||||
|
code: inputCredentials.code,
|
||||||
|
scope: `openid offline_access https://graph.microsoft.com/.default`,
|
||||||
|
client_id: INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_ID,
|
||||||
|
client_secret: INF_APP_CONNECTION_AZURE_CLIENT_SECRETS_CLIENT_SECRET,
|
||||||
|
redirect_uri: `${SITE_URL}/organization/app-connections/azure/oauth/callback`
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof AxiosError) {
|
||||||
|
tokenError = e;
|
||||||
|
} else {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Unable to validate connection: verify credentials`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenError) {
|
||||||
|
if (tokenError instanceof AxiosError) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Failed to get access token: ${
|
||||||
|
(tokenError?.response?.data as { error_description?: string })?.error_description || "Unknown error"
|
||||||
|
}`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new InternalServerError({
|
||||||
|
message: "Failed to get access token"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tokenResp) {
|
||||||
|
throw new InternalServerError({
|
||||||
|
message: `Failed to get access token: Token was empty with no error`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tenantId: inputCredentials.tenantId,
|
tenantId: inputCredentials.tenantId,
|
||||||
accessToken: tokenResp.data.access_token,
|
accessToken: tokenResp.data.access_token,
|
||||||
refreshToken: tokenResp.data.refresh_token,
|
refreshToken: tokenResp.data.refresh_token,
|
||||||
expiresAt: Date.now() + tokenResp.data.expires_in * 1000
|
expiresAt: Date.now() + tokenResp.data.expires_in * 1000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
case AzureClientSecretsConnectionMethod.ClientSecret:
|
||||||
|
const { tenantId, clientId, clientSecret } = inputCredentials;
|
||||||
|
try {
|
||||||
|
const { data: clientData } = await request.post<ExchangeCodeAzureResponse>(
|
||||||
|
IntegrationUrls.AZURE_TOKEN_URL.replace("common", tenantId || "common"),
|
||||||
|
new URLSearchParams({
|
||||||
|
grant_type: "client_credentials",
|
||||||
|
scope: `https://graph.microsoft.com/.default`,
|
||||||
|
client_id: clientId,
|
||||||
|
client_secret: clientSecret
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
tenantId,
|
||||||
|
accessToken: clientData.access_token,
|
||||||
|
expiresAt: Date.now() + clientData.expires_in * 1000,
|
||||||
|
clientId,
|
||||||
|
clientSecret
|
||||||
|
};
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof AxiosError) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `Failed to get access token: ${
|
||||||
|
(e?.response?.data as { error_description?: string })?.error_description || "Unknown error"
|
||||||
|
}`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new InternalServerError({
|
||||||
|
message: "Failed to get access token"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new InternalServerError({
|
throw new InternalServerError({
|
||||||
message: `Unhandled Azure connection method: ${method as AzureClientSecretsConnectionMethod}`
|
message: `Unhandled Azure connection method: ${method as AzureClientSecretsConnectionMethod}`
|
||||||
|
@@ -26,6 +26,36 @@ export const AzureClientSecretsConnectionOAuthOutputCredentialsSchema = z.object
|
|||||||
expiresAt: z.number()
|
expiresAt: z.number()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const AzureClientSecretsConnectionClientSecretInputCredentialsSchema = z.object({
|
||||||
|
clientId: z
|
||||||
|
.string()
|
||||||
|
.uuid()
|
||||||
|
.trim()
|
||||||
|
.min(1, "Client ID required")
|
||||||
|
.max(50, "Client ID must be at most 50 characters long")
|
||||||
|
.describe(AppConnections.CREDENTIALS.AZURE_CLIENT_SECRETS.clientId),
|
||||||
|
clientSecret: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(1, "Client Secret required")
|
||||||
|
.max(50, "Client Secret must be at most 50 characters long")
|
||||||
|
.describe(AppConnections.CREDENTIALS.AZURE_CLIENT_SECRETS.clientSecret),
|
||||||
|
tenantId: z
|
||||||
|
.string()
|
||||||
|
.uuid()
|
||||||
|
.trim()
|
||||||
|
.min(1, "Tenant ID required")
|
||||||
|
.describe(AppConnections.CREDENTIALS.AZURE_CLIENT_SECRETS.tenantId)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const AzureClientSecretsConnectionClientSecretOutputCredentialsSchema = z.object({
|
||||||
|
clientId: z.string(),
|
||||||
|
clientSecret: z.string(),
|
||||||
|
tenantId: z.string(),
|
||||||
|
accessToken: z.string(),
|
||||||
|
expiresAt: z.number()
|
||||||
|
});
|
||||||
|
|
||||||
export const ValidateAzureClientSecretsConnectionCredentialsSchema = z.discriminatedUnion("method", [
|
export const ValidateAzureClientSecretsConnectionCredentialsSchema = z.discriminatedUnion("method", [
|
||||||
z.object({
|
z.object({
|
||||||
method: z
|
method: z
|
||||||
@@ -34,6 +64,14 @@ export const ValidateAzureClientSecretsConnectionCredentialsSchema = z.discrimin
|
|||||||
credentials: AzureClientSecretsConnectionOAuthInputCredentialsSchema.describe(
|
credentials: AzureClientSecretsConnectionOAuthInputCredentialsSchema.describe(
|
||||||
AppConnections.CREATE(AppConnection.AzureClientSecrets).credentials
|
AppConnections.CREATE(AppConnection.AzureClientSecrets).credentials
|
||||||
)
|
)
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
method: z
|
||||||
|
.literal(AzureClientSecretsConnectionMethod.ClientSecret)
|
||||||
|
.describe(AppConnections.CREATE(AppConnection.AzureClientSecrets).method),
|
||||||
|
credentials: AzureClientSecretsConnectionClientSecretInputCredentialsSchema.describe(
|
||||||
|
AppConnections.CREATE(AppConnection.AzureClientSecrets).credentials
|
||||||
|
)
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -43,9 +81,13 @@ export const CreateAzureClientSecretsConnectionSchema = ValidateAzureClientSecre
|
|||||||
|
|
||||||
export const UpdateAzureClientSecretsConnectionSchema = z
|
export const UpdateAzureClientSecretsConnectionSchema = z
|
||||||
.object({
|
.object({
|
||||||
credentials: AzureClientSecretsConnectionOAuthInputCredentialsSchema.optional().describe(
|
credentials: z
|
||||||
AppConnections.UPDATE(AppConnection.AzureClientSecrets).credentials
|
.union([
|
||||||
)
|
AzureClientSecretsConnectionOAuthInputCredentialsSchema,
|
||||||
|
AzureClientSecretsConnectionClientSecretInputCredentialsSchema
|
||||||
|
])
|
||||||
|
.optional()
|
||||||
|
.describe(AppConnections.UPDATE(AppConnection.AzureClientSecrets).credentials)
|
||||||
})
|
})
|
||||||
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.AzureClientSecrets));
|
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.AzureClientSecrets));
|
||||||
|
|
||||||
@@ -59,6 +101,10 @@ export const AzureClientSecretsConnectionSchema = z.intersection(
|
|||||||
z.object({
|
z.object({
|
||||||
method: z.literal(AzureClientSecretsConnectionMethod.OAuth),
|
method: z.literal(AzureClientSecretsConnectionMethod.OAuth),
|
||||||
credentials: AzureClientSecretsConnectionOAuthOutputCredentialsSchema
|
credentials: AzureClientSecretsConnectionOAuthOutputCredentialsSchema
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
method: z.literal(AzureClientSecretsConnectionMethod.ClientSecret),
|
||||||
|
credentials: AzureClientSecretsConnectionClientSecretOutputCredentialsSchema
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
@@ -69,6 +115,13 @@ export const SanitizedAzureClientSecretsConnectionSchema = z.discriminatedUnion(
|
|||||||
credentials: AzureClientSecretsConnectionOAuthOutputCredentialsSchema.pick({
|
credentials: AzureClientSecretsConnectionOAuthOutputCredentialsSchema.pick({
|
||||||
tenantId: true
|
tenantId: true
|
||||||
})
|
})
|
||||||
|
}),
|
||||||
|
BaseAzureClientSecretsConnectionSchema.extend({
|
||||||
|
method: z.literal(AzureClientSecretsConnectionMethod.ClientSecret),
|
||||||
|
credentials: AzureClientSecretsConnectionClientSecretOutputCredentialsSchema.pick({
|
||||||
|
clientId: true,
|
||||||
|
tenantId: true
|
||||||
|
})
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@@ -4,6 +4,7 @@ import { DiscriminativePick } from "@app/lib/types";
|
|||||||
|
|
||||||
import { AppConnection } from "../app-connection-enums";
|
import { AppConnection } from "../app-connection-enums";
|
||||||
import {
|
import {
|
||||||
|
AzureClientSecretsConnectionClientSecretOutputCredentialsSchema,
|
||||||
AzureClientSecretsConnectionOAuthOutputCredentialsSchema,
|
AzureClientSecretsConnectionOAuthOutputCredentialsSchema,
|
||||||
AzureClientSecretsConnectionSchema,
|
AzureClientSecretsConnectionSchema,
|
||||||
CreateAzureClientSecretsConnectionSchema,
|
CreateAzureClientSecretsConnectionSchema,
|
||||||
@@ -30,6 +31,10 @@ export type TAzureClientSecretsConnectionCredentials = z.infer<
|
|||||||
typeof AzureClientSecretsConnectionOAuthOutputCredentialsSchema
|
typeof AzureClientSecretsConnectionOAuthOutputCredentialsSchema
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
export type TAzureClientSecretsConnectionClientSecretCredentials = z.infer<
|
||||||
|
typeof AzureClientSecretsConnectionClientSecretOutputCredentialsSchema
|
||||||
|
>;
|
||||||
|
|
||||||
export interface ExchangeCodeAzureResponse {
|
export interface ExchangeCodeAzureResponse {
|
||||||
token_type: string;
|
token_type: string;
|
||||||
scope: string;
|
scope: string;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user