mirror of
https://github.com/Infisical/infisical.git
synced 2025-07-05 04:29:09 +00:00
Compare commits
156 Commits
misc/add-p
...
identity-a
Author | SHA1 | Date | |
---|---|---|---|
a534a4975c | |||
79a616dc1c | |||
598d14fc54 | |||
99c9b644df | |||
94aed485a5 | |||
e382941424 | |||
89c6ab591a | |||
235a33a01c | |||
dd6c217dc8 | |||
78b1b5583a | |||
8f2a504fd0 | |||
1d5b629d8f | |||
14f895cae2 | |||
b7be6bd1d9 | |||
58a97852f6 | |||
980aa9eaae | |||
a35d1aa72b | |||
52d801bce5 | |||
c92c160709 | |||
71ca7a82db | |||
6f799b478d | |||
a89e6b6e58 | |||
99ca9e04f8 | |||
6cdc71b9b1 | |||
f88d6a183f | |||
fa82d4953e | |||
12d9fe9ffd | |||
86acf88a13 | |||
63c7c39e21 | |||
151edc7efa | |||
5fa7f56285 | |||
810b27d121 | |||
51fe7450ae | |||
938c06a2ed | |||
38d431ec77 | |||
d202fdf5c8 | |||
f1b2028542 | |||
5c9b46dfba | |||
a516e50984 | |||
3c1fc024c2 | |||
569439f208 | |||
9afc282679 | |||
8db85cfb84 | |||
664b2f0089 | |||
5e9bd3a7c6 | |||
2c13af6db3 | |||
ec9171d0bc | |||
81362bec8f | |||
5a4d7541a2 | |||
3c97c45455 | |||
4f015d77fb | |||
78e894c2bb | |||
23513158ed | |||
934ef8ab27 | |||
23e9c52f67 | |||
e276752e7c | |||
01ae19fa2b | |||
9df8cf60ef | |||
1b1fe2a700 | |||
338961480c | |||
03debcab5a | |||
4a6f759900 | |||
b9d06ff686 | |||
5cc5a4f03d | |||
5ef2be1a9c | |||
8de9ddfb8b | |||
5b40de16cf | |||
11aac3f5dc | |||
9823c7d1aa | |||
d627ecf05d | |||
3ba396f7fa | |||
9c561266ed | |||
36fef11d91 | |||
742932c4a0 | |||
57a77ae5f1 | |||
7c9564c7dc | |||
736aecebf8 | |||
16748357d7 | |||
12863b389b | |||
6341b7e989 | |||
c592ff00a6 | |||
ef87086272 | |||
bd459d994c | |||
440f93f392 | |||
bc32d6cbbf | |||
0cf3115830 | |||
65f2e626ae | |||
8b3e3152a4 | |||
661b31f762 | |||
e78ad1147b | |||
473efa91f0 | |||
b440e918ac | |||
439f253350 | |||
4e68304262 | |||
c4d0896609 | |||
b8115d481c | |||
5fdec97319 | |||
755bb1679a | |||
7142e7a6c6 | |||
11ade92b5b | |||
4147725260 | |||
c359cf162f | |||
9d04b648fa | |||
9edfdb7234 | |||
ff74e020fc | |||
6ee446e574 | |||
c806059b11 | |||
3a5bb31bde | |||
6f38d6c76f | |||
d721a46ec9 | |||
989065ba34 | |||
5ad419c079 | |||
80f72e8040 | |||
0cef728617 | |||
0a735422ed | |||
8370a0d9c0 | |||
71af662998 | |||
27e391f7e0 | |||
2187c7588c | |||
8ab7e8360d | |||
957cab3117 | |||
4bf16f68fc | |||
ab3ee775bb | |||
2a86e6f4d1 | |||
194fbb79f2 | |||
faaba8deb7 | |||
ae21b157a9 | |||
6167c70a74 | |||
0ecf75cbdb | |||
3f8aa0fa4b | |||
6487c83bda | |||
c08fbbdab2 | |||
a4aa65bb81 | |||
258d19cbe4 | |||
de91356127 | |||
ccb07942de | |||
3d278b0925 | |||
956fb2efb4 | |||
13894261ce | |||
d7ffa70906 | |||
b8fa7c5bb6 | |||
2baacfcd8f | |||
31c11f7d2a | |||
c5f06dece4 | |||
662e79ac98 | |||
17249d603b | |||
9bdff9c504 | |||
4552ce6ca4 | |||
ba4b8801eb | |||
36a5f728a1 | |||
502429d914 | |||
27abfa4fff | |||
4d43accc8a | |||
3c89a69410 | |||
e741b63e63 | |||
60749cfc43 |
58
.github/workflows/deployment-pipeline.yml
vendored
58
.github/workflows/deployment-pipeline.yml
vendored
@ -5,6 +5,10 @@ permissions:
|
|||||||
id-token: write
|
id-token: write
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: "infisical-core-deployment"
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
infisical-tests:
|
infisical-tests:
|
||||||
name: Integration tests
|
name: Integration tests
|
||||||
@ -113,10 +117,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: twingate/github-action@v1
|
- uses: twingate/github-action@v1
|
||||||
with:
|
with:
|
||||||
# The Twingate Service Key used to connect Twingate to the proper service
|
|
||||||
# Learn more about [Twingate Services](https://docs.twingate.com/docs/services)
|
|
||||||
#
|
|
||||||
# Required
|
|
||||||
service-key: ${{ secrets.TWINGATE_SERVICE_KEY }}
|
service-key: ${{ secrets.TWINGATE_SERVICE_KEY }}
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@ -159,6 +159,31 @@ jobs:
|
|||||||
service: infisical-core-platform
|
service: infisical-core-platform
|
||||||
cluster: infisical-core-platform
|
cluster: infisical-core-platform
|
||||||
wait-for-service-stability: true
|
wait-for-service-stability: true
|
||||||
|
- name: Post slack message
|
||||||
|
uses: slackapi/slack-github-action@v2.0.0
|
||||||
|
with:
|
||||||
|
webhook: ${{ secrets.SLACK_DEPLOYMENT_WEBHOOK_URL }}
|
||||||
|
webhook-type: incoming-webhook
|
||||||
|
payload: |
|
||||||
|
text: "*Deployment Status Update*: ${{ job.status }}"
|
||||||
|
blocks:
|
||||||
|
- type: "section"
|
||||||
|
text:
|
||||||
|
type: "mrkdwn"
|
||||||
|
text: "*Deployment Status Update*: ${{ job.status }}"
|
||||||
|
- type: "section"
|
||||||
|
fields:
|
||||||
|
- type: "mrkdwn"
|
||||||
|
text: "*Application:*\nInfisical Core"
|
||||||
|
- type: "mrkdwn"
|
||||||
|
text: "*Instance Type:*\nShared Infisical Cloud"
|
||||||
|
- type: "section"
|
||||||
|
fields:
|
||||||
|
- type: "mrkdwn"
|
||||||
|
text: "*Region:*\nUS"
|
||||||
|
- type: "mrkdwn"
|
||||||
|
text: "*Git Tag:*\n<https://github.com/Infisical/infisical/commit/${{ steps.commit.outputs.short }}>"
|
||||||
|
|
||||||
|
|
||||||
production-eu:
|
production-eu:
|
||||||
name: EU production deploy
|
name: EU production deploy
|
||||||
@ -210,3 +235,28 @@ jobs:
|
|||||||
service: infisical-core-platform
|
service: infisical-core-platform
|
||||||
cluster: infisical-core-platform
|
cluster: infisical-core-platform
|
||||||
wait-for-service-stability: true
|
wait-for-service-stability: true
|
||||||
|
- name: Post slack message
|
||||||
|
uses: slackapi/slack-github-action@v2.0.0
|
||||||
|
with:
|
||||||
|
webhook: ${{ secrets.SLACK_DEPLOYMENT_WEBHOOK_URL }}
|
||||||
|
webhook-type: incoming-webhook
|
||||||
|
payload: |
|
||||||
|
text: "*Deployment Status Update*: ${{ job.status }}"
|
||||||
|
blocks:
|
||||||
|
- type: "section"
|
||||||
|
text:
|
||||||
|
type: "mrkdwn"
|
||||||
|
text: "*Deployment Status Update*: ${{ job.status }}"
|
||||||
|
- type: "section"
|
||||||
|
fields:
|
||||||
|
- type: "mrkdwn"
|
||||||
|
text: "*Application:*\nInfisical Core"
|
||||||
|
- type: "mrkdwn"
|
||||||
|
text: "*Instance Type:*\nShared Infisical Cloud"
|
||||||
|
- type: "section"
|
||||||
|
fields:
|
||||||
|
- type: "mrkdwn"
|
||||||
|
text: "*Region:*\nEU"
|
||||||
|
- type: "mrkdwn"
|
||||||
|
text: "*Git Tag:*\n<https://github.com/Infisical/infisical/commit/${{ steps.commit.outputs.short }}>"
|
||||||
|
|
||||||
|
@ -7,3 +7,4 @@ docs/self-hosting/configuration/envars.mdx:generic-api-key:106
|
|||||||
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:451
|
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:451
|
||||||
docs/mint.json:generic-api-key:651
|
docs/mint.json:generic-api-key:651
|
||||||
backend/src/ee/services/hsm/hsm-service.ts:generic-api-key:134
|
backend/src/ee/services/hsm/hsm-service.ts:generic-api-key:134
|
||||||
|
docs/documentation/platform/audit-log-streams/audit-log-streams.mdx:generic-api-key:104
|
||||||
|
@ -56,7 +56,7 @@ We're on a mission to make security tooling more accessible to everyone, not jus
|
|||||||
- **[Infisical Kubernetes Operator](https://infisical.com/docs/documentation/getting-started/kubernetes)**: Deliver secrets to your Kubernetes workloads and automatically reload deployments.
|
- **[Infisical Kubernetes Operator](https://infisical.com/docs/documentation/getting-started/kubernetes)**: Deliver secrets to your Kubernetes workloads and automatically reload deployments.
|
||||||
- **[Infisical Agent](https://infisical.com/docs/infisical-agent/overview)**: Inject secrets into applications without modifying any code logic.
|
- **[Infisical Agent](https://infisical.com/docs/infisical-agent/overview)**: Inject secrets into applications without modifying any code logic.
|
||||||
|
|
||||||
### Internal PKI:
|
### Infisical (Internal) PKI:
|
||||||
|
|
||||||
- **[Private Certificate Authority](https://infisical.com/docs/documentation/platform/pki/private-ca)**: Create CA hierarchies, configure [certificate templates](https://infisical.com/docs/documentation/platform/pki/certificates#guide-to-issuing-certificates) for policy enforcement, and start issuing X.509 certificates.
|
- **[Private Certificate Authority](https://infisical.com/docs/documentation/platform/pki/private-ca)**: Create CA hierarchies, configure [certificate templates](https://infisical.com/docs/documentation/platform/pki/certificates#guide-to-issuing-certificates) for policy enforcement, and start issuing X.509 certificates.
|
||||||
- **[Certificate Management](https://infisical.com/docs/documentation/platform/pki/certificates)**: Manage the certificate lifecycle from [issuance](https://infisical.com/docs/documentation/platform/pki/certificates#guide-to-issuing-certificates) to [revocation](https://infisical.com/docs/documentation/platform/pki/certificates#guide-to-revoking-certificates) with support for CRL.
|
- **[Certificate Management](https://infisical.com/docs/documentation/platform/pki/certificates)**: Manage the certificate lifecycle from [issuance](https://infisical.com/docs/documentation/platform/pki/certificates#guide-to-issuing-certificates) to [revocation](https://infisical.com/docs/documentation/platform/pki/certificates#guide-to-revoking-certificates) with support for CRL.
|
||||||
@ -64,12 +64,17 @@ We're on a mission to make security tooling more accessible to everyone, not jus
|
|||||||
- **[Infisical PKI Issuer for Kubernetes](https://infisical.com/docs/documentation/platform/pki/pki-issuer)**: Deliver TLS certificates to your Kubernetes workloads with automatic renewal.
|
- **[Infisical PKI Issuer for Kubernetes](https://infisical.com/docs/documentation/platform/pki/pki-issuer)**: Deliver TLS certificates to your Kubernetes workloads with automatic renewal.
|
||||||
- **[Enrollment over Secure Transport](https://infisical.com/docs/documentation/platform/pki/est)**: Enroll and manage certificates via EST protocol.
|
- **[Enrollment over Secure Transport](https://infisical.com/docs/documentation/platform/pki/est)**: Enroll and manage certificates via EST protocol.
|
||||||
|
|
||||||
### Key Management (KMS):
|
### Infisical Key Management System (KMS):
|
||||||
|
|
||||||
- **[Cryptographic Keys](https://infisical.com/docs/documentation/platform/kms)**: Centrally manage keys across projects through a user-friendly interface or via the API.
|
- **[Cryptographic Keys](https://infisical.com/docs/documentation/platform/kms)**: Centrally manage keys across projects through a user-friendly interface or via the API.
|
||||||
- **[Encrypt and Decrypt Data](https://infisical.com/docs/documentation/platform/kms#guide-to-encrypting-data)**: Use symmetric keys to encrypt and decrypt data.
|
- **[Encrypt and Decrypt Data](https://infisical.com/docs/documentation/platform/kms#guide-to-encrypting-data)**: Use symmetric keys to encrypt and decrypt data.
|
||||||
|
|
||||||
|
### Infisical SSH
|
||||||
|
|
||||||
|
- **[Signed SSH Certificates](https://infisical.com/docs/documentation/platform/ssh)**: Issue ephemeral SSH credentials for secure, short-lived, and centralized access to infrastructure.
|
||||||
|
|
||||||
### General Platform:
|
### General Platform:
|
||||||
|
|
||||||
- **Authentication Methods**: Authenticate machine identities with Infisical using a cloud-native or platform agnostic authentication method ([Kubernetes Auth](https://infisical.com/docs/documentation/platform/identities/kubernetes-auth), [GCP Auth](https://infisical.com/docs/documentation/platform/identities/gcp-auth), [Azure Auth](https://infisical.com/docs/documentation/platform/identities/azure-auth), [AWS Auth](https://infisical.com/docs/documentation/platform/identities/aws-auth), [OIDC Auth](https://infisical.com/docs/documentation/platform/identities/oidc-auth/general), [Universal Auth](https://infisical.com/docs/documentation/platform/identities/universal-auth)).
|
- **Authentication Methods**: Authenticate machine identities with Infisical using a cloud-native or platform agnostic authentication method ([Kubernetes Auth](https://infisical.com/docs/documentation/platform/identities/kubernetes-auth), [GCP Auth](https://infisical.com/docs/documentation/platform/identities/gcp-auth), [Azure Auth](https://infisical.com/docs/documentation/platform/identities/azure-auth), [AWS Auth](https://infisical.com/docs/documentation/platform/identities/aws-auth), [OIDC Auth](https://infisical.com/docs/documentation/platform/identities/oidc-auth/general), [Universal Auth](https://infisical.com/docs/documentation/platform/identities/universal-auth)).
|
||||||
- **[Access Controls](https://infisical.com/docs/documentation/platform/access-controls/overview)**: Define advanced authorization controls for users and machine identities with [RBAC](https://infisical.com/docs/documentation/platform/access-controls/role-based-access-controls), [additional privileges](https://infisical.com/docs/documentation/platform/access-controls/additional-privileges), [temporary access](https://infisical.com/docs/documentation/platform/access-controls/temporary-access), [access requests](https://infisical.com/docs/documentation/platform/access-controls/access-requests), [approval workflows](https://infisical.com/docs/documentation/platform/pr-workflows), and more.
|
- **[Access Controls](https://infisical.com/docs/documentation/platform/access-controls/overview)**: Define advanced authorization controls for users and machine identities with [RBAC](https://infisical.com/docs/documentation/platform/access-controls/role-based-access-controls), [additional privileges](https://infisical.com/docs/documentation/platform/access-controls/additional-privileges), [temporary access](https://infisical.com/docs/documentation/platform/access-controls/temporary-access), [access requests](https://infisical.com/docs/documentation/platform/access-controls/access-requests), [approval workflows](https://infisical.com/docs/documentation/platform/pr-workflows), and more.
|
||||||
- **[Audit logs](https://infisical.com/docs/documentation/platform/audit-logs)**: Track every action taken on the platform.
|
- **[Audit logs](https://infisical.com/docs/documentation/platform/audit-logs)**: Track every action taken on the platform.
|
||||||
|
2
backend/src/@types/fastify.d.ts
vendored
2
backend/src/@types/fastify.d.ts
vendored
@ -80,6 +80,7 @@ import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-
|
|||||||
import { TSecretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
|
import { TSecretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
|
||||||
import { TSecretReplicationServiceFactory } from "@app/services/secret-replication/secret-replication-service";
|
import { TSecretReplicationServiceFactory } from "@app/services/secret-replication/secret-replication-service";
|
||||||
import { TSecretSharingServiceFactory } from "@app/services/secret-sharing/secret-sharing-service";
|
import { TSecretSharingServiceFactory } from "@app/services/secret-sharing/secret-sharing-service";
|
||||||
|
import { TSecretSyncServiceFactory } from "@app/services/secret-sync/secret-sync-service";
|
||||||
import { TSecretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service";
|
import { TSecretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service";
|
||||||
import { TServiceTokenServiceFactory } from "@app/services/service-token/service-token-service";
|
import { TServiceTokenServiceFactory } from "@app/services/service-token/service-token-service";
|
||||||
import { TSlackServiceFactory } from "@app/services/slack/slack-service";
|
import { TSlackServiceFactory } from "@app/services/slack/slack-service";
|
||||||
@ -210,6 +211,7 @@ declare module "fastify" {
|
|||||||
projectTemplate: TProjectTemplateServiceFactory;
|
projectTemplate: TProjectTemplateServiceFactory;
|
||||||
totp: TTotpServiceFactory;
|
totp: TTotpServiceFactory;
|
||||||
appConnection: TAppConnectionServiceFactory;
|
appConnection: TAppConnectionServiceFactory;
|
||||||
|
secretSync: TSecretSyncServiceFactory;
|
||||||
};
|
};
|
||||||
// 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
|
||||||
|
2
backend/src/@types/knex.d.ts
vendored
2
backend/src/@types/knex.d.ts
vendored
@ -372,6 +372,7 @@ import {
|
|||||||
TExternalGroupOrgRoleMappingsInsert,
|
TExternalGroupOrgRoleMappingsInsert,
|
||||||
TExternalGroupOrgRoleMappingsUpdate
|
TExternalGroupOrgRoleMappingsUpdate
|
||||||
} from "@app/db/schemas/external-group-org-role-mappings";
|
} from "@app/db/schemas/external-group-org-role-mappings";
|
||||||
|
import { TSecretSyncs, TSecretSyncsInsert, TSecretSyncsUpdate } from "@app/db/schemas/secret-syncs";
|
||||||
import {
|
import {
|
||||||
TSecretV2TagJunction,
|
TSecretV2TagJunction,
|
||||||
TSecretV2TagJunctionInsert,
|
TSecretV2TagJunctionInsert,
|
||||||
@ -900,5 +901,6 @@ declare module "knex/types/tables" {
|
|||||||
TAppConnectionsInsert,
|
TAppConnectionsInsert,
|
||||||
TAppConnectionsUpdate
|
TAppConnectionsUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.SecretSync]: KnexOriginal.CompositeTableType<TSecretSyncs, TSecretSyncsInsert, TSecretSyncsUpdate>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
// find any duplicate group names within organizations
|
||||||
|
const duplicates = await knex(TableName.Groups)
|
||||||
|
.select("orgId", "name")
|
||||||
|
.count("* as count")
|
||||||
|
.groupBy("orgId", "name")
|
||||||
|
.having(knex.raw("count(*) > 1"));
|
||||||
|
|
||||||
|
// for each set of duplicates, update all but one with a numbered suffix
|
||||||
|
for await (const duplicate of duplicates) {
|
||||||
|
const groups = await knex(TableName.Groups)
|
||||||
|
.select("id", "name")
|
||||||
|
.where({
|
||||||
|
orgId: duplicate.orgId,
|
||||||
|
name: duplicate.name
|
||||||
|
})
|
||||||
|
.orderBy("createdAt", "asc"); // keep original name for oldest group
|
||||||
|
|
||||||
|
// skip the first (oldest) group, rename others with numbered suffix
|
||||||
|
for (let i = 1; i < groups.length; i += 1) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await knex(TableName.Groups)
|
||||||
|
.where("id", groups[i].id)
|
||||||
|
.update({
|
||||||
|
name: `${groups[i].name} (${i})`,
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore TS doesn't know about Knex's timestamp types
|
||||||
|
updatedAt: new Date()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the unique constraint
|
||||||
|
await knex.schema.alterTable(TableName.Groups, (t) => {
|
||||||
|
t.unique(["orgId", "name"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
// Remove the unique constraint
|
||||||
|
await knex.schema.alterTable(TableName.Groups, (t) => {
|
||||||
|
t.dropUnique(["orgId", "name"]);
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasEnforceCapitalizationCol = await knex.schema.hasColumn(TableName.Project, "enforceCapitalization");
|
||||||
|
const hasAutoCapitalizationCol = await knex.schema.hasColumn(TableName.Project, "autoCapitalization");
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Project, (t) => {
|
||||||
|
if (!hasEnforceCapitalizationCol) {
|
||||||
|
t.boolean("enforceCapitalization").defaultTo(false).notNullable();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasAutoCapitalizationCol) {
|
||||||
|
t.boolean("autoCapitalization").defaultTo(false).alter();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasEnforceCapitalizationCol = await knex.schema.hasColumn(TableName.Project, "enforceCapitalization");
|
||||||
|
const hasAutoCapitalizationCol = await knex.schema.hasColumn(TableName.Project, "autoCapitalization");
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Project, (t) => {
|
||||||
|
if (hasEnforceCapitalizationCol) {
|
||||||
|
t.dropColumn("enforceCapitalization");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasAutoCapitalizationCol) {
|
||||||
|
t.boolean("autoCapitalization").defaultTo(true).alter();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
50
backend/src/db/migrations/20250122055102_secret-sync.ts
Normal file
50
backend/src/db/migrations/20250122055102_secret-sync.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "@app/db/schemas";
|
||||||
|
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "@app/db/utils";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasTable(TableName.SecretSync))) {
|
||||||
|
await knex.schema.createTable(TableName.SecretSync, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.string("name", 32).notNullable();
|
||||||
|
t.string("description");
|
||||||
|
t.string("destination").notNullable();
|
||||||
|
t.boolean("isAutoSyncEnabled").notNullable().defaultTo(true);
|
||||||
|
t.integer("version").defaultTo(1).notNullable();
|
||||||
|
t.jsonb("destinationConfig").notNullable();
|
||||||
|
t.jsonb("syncOptions").notNullable();
|
||||||
|
// we're including projectId in addition to folder ID because we allow folderId to be null (if the folder
|
||||||
|
// is deleted), to preserve sync configuration
|
||||||
|
t.string("projectId").notNullable();
|
||||||
|
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
||||||
|
t.uuid("folderId");
|
||||||
|
t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("SET NULL");
|
||||||
|
t.uuid("connectionId").notNullable();
|
||||||
|
t.foreign("connectionId").references("id").inTable(TableName.AppConnection);
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
// sync secrets to destination
|
||||||
|
t.string("syncStatus");
|
||||||
|
t.string("lastSyncJobId");
|
||||||
|
t.string("lastSyncMessage");
|
||||||
|
t.datetime("lastSyncedAt");
|
||||||
|
// import secrets from destination
|
||||||
|
t.string("importStatus");
|
||||||
|
t.string("lastImportJobId");
|
||||||
|
t.string("lastImportMessage");
|
||||||
|
t.datetime("lastImportedAt");
|
||||||
|
// remove secrets from destination
|
||||||
|
t.string("removeStatus");
|
||||||
|
t.string("lastRemoveJobId");
|
||||||
|
t.string("lastRemoveMessage");
|
||||||
|
t.datetime("lastRemovedAt");
|
||||||
|
});
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.SecretSync);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.SecretSync);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.SecretSync);
|
||||||
|
}
|
@ -131,7 +131,8 @@ export enum TableName {
|
|||||||
WorkflowIntegrations = "workflow_integrations",
|
WorkflowIntegrations = "workflow_integrations",
|
||||||
SlackIntegrations = "slack_integrations",
|
SlackIntegrations = "slack_integrations",
|
||||||
ProjectSlackConfigs = "project_slack_configs",
|
ProjectSlackConfigs = "project_slack_configs",
|
||||||
AppConnection = "app_connections"
|
AppConnection = "app_connections",
|
||||||
|
SecretSync = "secret_syncs"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
|
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
|
||||||
@ -215,3 +216,12 @@ export enum ProjectType {
|
|||||||
KMS = "kms",
|
KMS = "kms",
|
||||||
SSH = "ssh"
|
SSH = "ssh"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ActionProjectType {
|
||||||
|
SecretManager = ProjectType.SecretManager,
|
||||||
|
CertificateManager = ProjectType.CertificateManager,
|
||||||
|
KMS = ProjectType.KMS,
|
||||||
|
SSH = ProjectType.SSH,
|
||||||
|
// project operations that happen on all types
|
||||||
|
Any = "any"
|
||||||
|
}
|
||||||
|
@ -13,7 +13,7 @@ export const ProjectsSchema = z.object({
|
|||||||
id: z.string(),
|
id: z.string(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
slug: z.string(),
|
slug: z.string(),
|
||||||
autoCapitalization: z.boolean().default(true).nullable().optional(),
|
autoCapitalization: z.boolean().default(false).nullable().optional(),
|
||||||
orgId: z.string().uuid(),
|
orgId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
@ -25,7 +25,8 @@ 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()
|
type: z.string(),
|
||||||
|
enforceCapitalization: z.boolean().default(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TProjects = z.infer<typeof ProjectsSchema>;
|
export type TProjects = z.infer<typeof ProjectsSchema>;
|
||||||
|
40
backend/src/db/schemas/secret-syncs.ts
Normal file
40
backend/src/db/schemas/secret-syncs.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// 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 SecretSyncsSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
name: z.string(),
|
||||||
|
description: z.string().nullable().optional(),
|
||||||
|
destination: z.string(),
|
||||||
|
isAutoSyncEnabled: z.boolean().default(true),
|
||||||
|
version: z.number().default(1),
|
||||||
|
destinationConfig: z.unknown(),
|
||||||
|
syncOptions: z.unknown(),
|
||||||
|
projectId: z.string(),
|
||||||
|
folderId: z.string().uuid().nullable().optional(),
|
||||||
|
connectionId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date(),
|
||||||
|
syncStatus: z.string().nullable().optional(),
|
||||||
|
lastSyncJobId: z.string().nullable().optional(),
|
||||||
|
lastSyncMessage: z.string().nullable().optional(),
|
||||||
|
lastSyncedAt: z.date().nullable().optional(),
|
||||||
|
importStatus: z.string().nullable().optional(),
|
||||||
|
lastImportJobId: z.string().nullable().optional(),
|
||||||
|
lastImportMessage: z.string().nullable().optional(),
|
||||||
|
lastImportedAt: z.date().nullable().optional(),
|
||||||
|
removeStatus: z.string().nullable().optional(),
|
||||||
|
lastRemoveJobId: z.string().nullable().optional(),
|
||||||
|
lastRemoveMessage: z.string().nullable().optional(),
|
||||||
|
lastRemovedAt: z.date().nullable().optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TSecretSyncs = z.infer<typeof SecretSyncsSchema>;
|
||||||
|
export type TSecretSyncsInsert = Omit<z.input<typeof SecretSyncsSchema>, TImmutableDBKeys>;
|
||||||
|
export type TSecretSyncsUpdate = Partial<Omit<z.input<typeof SecretSyncsSchema>, TImmutableDBKeys>>;
|
@ -24,6 +24,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
),
|
),
|
||||||
name: z.string().trim(),
|
name: z.string().trim(),
|
||||||
description: z.string().trim().nullish(),
|
description: z.string().trim().nullish(),
|
||||||
|
// TODO(scott): once UI refactored permissions: OrgPermissionSchema.array()
|
||||||
permissions: z.any().array()
|
permissions: z.any().array()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
@ -96,6 +97,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
.optional(),
|
.optional(),
|
||||||
name: z.string().trim().optional(),
|
name: z.string().trim().optional(),
|
||||||
description: z.string().trim().nullish(),
|
description: z.string().trim().nullish(),
|
||||||
|
// TODO(scott): once UI refactored permissions: OrgPermissionSchema.array().optional()
|
||||||
permissions: z.any().array().optional()
|
permissions: z.any().array().optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { GitAppOrgSchema, SecretScanningGitRisksSchema } from "@app/db/schemas";
|
import { GitAppOrgSchema, SecretScanningGitRisksSchema } from "@app/db/schemas";
|
||||||
import { SecretScanningRiskStatus } from "@app/ee/services/secret-scanning/secret-scanning-types";
|
import {
|
||||||
|
SecretScanningResolvedStatus,
|
||||||
|
SecretScanningRiskStatus
|
||||||
|
} from "@app/ee/services/secret-scanning/secret-scanning-types";
|
||||||
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 { OrderByDirection } from "@app/lib/types";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
@ -97,6 +101,45 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/organization/:organizationId/risks/export",
|
||||||
|
method: "GET",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
|
querystring: z.object({
|
||||||
|
repositoryNames: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.nullable()
|
||||||
|
.transform((val) => (val ? val.split(",") : undefined)),
|
||||||
|
resolvedStatus: z.nativeEnum(SecretScanningResolvedStatus).optional()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
risks: SecretScanningGitRisksSchema.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const risks = await server.services.secretScanning.getAllRisksByOrg({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
orgId: req.params.organizationId,
|
||||||
|
filter: {
|
||||||
|
repositoryNames: req.query.repositoryNames,
|
||||||
|
resolvedStatus: req.query.resolvedStatus
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { risks };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/organization/:organizationId/risks",
|
url: "/organization/:organizationId/risks",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@ -105,20 +148,46 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
|||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({ organizationId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
|
|
||||||
|
querystring: z.object({
|
||||||
|
offset: z.coerce.number().min(0).default(0),
|
||||||
|
limit: z.coerce.number().min(1).max(20000).default(100),
|
||||||
|
orderBy: z.enum(["createdAt", "name"]).default("createdAt"),
|
||||||
|
orderDirection: z.nativeEnum(OrderByDirection).default(OrderByDirection.DESC),
|
||||||
|
repositoryNames: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.nullable()
|
||||||
|
.transform((val) => (val ? val.split(",") : undefined)),
|
||||||
|
resolvedStatus: z.nativeEnum(SecretScanningResolvedStatus).optional()
|
||||||
|
}),
|
||||||
|
|
||||||
response: {
|
response: {
|
||||||
200: z.object({ risks: SecretScanningGitRisksSchema.array() })
|
200: z.object({
|
||||||
|
risks: SecretScanningGitRisksSchema.array(),
|
||||||
|
totalCount: z.number(),
|
||||||
|
repos: z.array(z.string())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { risks } = await server.services.secretScanning.getRisksByOrg({
|
const { risks, totalCount, repos } = await server.services.secretScanning.getRisksByOrg({
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
orgId: req.params.organizationId
|
orgId: req.params.organizationId,
|
||||||
|
filter: {
|
||||||
|
limit: req.query.limit,
|
||||||
|
offset: req.query.offset,
|
||||||
|
orderBy: req.query.orderBy,
|
||||||
|
orderDirection: req.query.orderDirection,
|
||||||
|
repositoryNames: req.query.repositoryNames,
|
||||||
|
resolvedStatus: req.query.resolvedStatus
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return { risks };
|
return { risks, totalCount, repos };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
import { ProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
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";
|
||||||
@ -87,14 +87,14 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
if (!groupApprovers && approvals > userApprovers.length + userApproverNames.length)
|
if (!groupApprovers && approvals > userApprovers.length + userApproverNames.length)
|
||||||
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
project.id,
|
projectId: project.id,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
@ -193,7 +193,14 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
// Anyone in the project should be able to get the policies.
|
// Anyone in the project should be able to get the policies.
|
||||||
await permissionService.getProjectPermission(actor, actorId, project.id, actorAuthMethod, actorOrgId);
|
await permissionService.getProjectPermission({
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
projectId: project.id,
|
||||||
|
actorAuthMethod,
|
||||||
|
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;
|
||||||
@ -237,14 +244,14 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
if (!accessApprovalPolicy) {
|
if (!accessApprovalPolicy) {
|
||||||
throw new NotFoundError({ message: `Secret approval policy with ID '${policyId}' not found` });
|
throw new NotFoundError({ message: `Secret approval policy with ID '${policyId}' not found` });
|
||||||
}
|
}
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
accessApprovalPolicy.projectId,
|
projectId: accessApprovalPolicy.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
||||||
|
|
||||||
@ -321,14 +328,14 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
const policy = await accessApprovalPolicyDAL.findById(policyId);
|
const policy = await accessApprovalPolicyDAL.findById(policyId);
|
||||||
if (!policy) throw new NotFoundError({ message: `Secret approval policy with ID '${policyId}' not found` });
|
if (!policy) throw new NotFoundError({ message: `Secret approval policy with ID '${policyId}' not found` });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
policy.projectId,
|
projectId: policy.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Delete,
|
ProjectPermissionActions.Delete,
|
||||||
ProjectPermissionSub.SecretApproval
|
ProjectPermissionSub.SecretApproval
|
||||||
@ -372,13 +379,14 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
|
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
const { membership } = await permissionService.getProjectPermission(
|
const { membership } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
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" });
|
||||||
}
|
}
|
||||||
@ -411,13 +419,14 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
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);
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
import ms 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 { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
@ -100,13 +100,14 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
// Anyone can create an access approval request.
|
// Anyone can create an access approval request.
|
||||||
const { membership } = await permissionService.getProjectPermission(
|
const { membership } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
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" });
|
||||||
}
|
}
|
||||||
@ -273,13 +274,14 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
const { membership } = await permissionService.getProjectPermission(
|
const { membership } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
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" });
|
||||||
}
|
}
|
||||||
@ -318,13 +320,14 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission(
|
const { membership, hasRole } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
accessApprovalRequest.projectId,
|
projectId: accessApprovalRequest.projectId,
|
||||||
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" });
|
||||||
@ -422,13 +425,14 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
const { membership } = await permissionService.getProjectPermission(
|
const { membership } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
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" });
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
throw new Error(`Failed to connect with the source ${(err as Error)?.message}`);
|
throw new BadRequestError({ message: `Failed to connect with upstream source: ${(err as Error)?.message}` });
|
||||||
});
|
});
|
||||||
const encryptedHeaders = headers ? infisicalSymmetricEncypt(JSON.stringify(headers)) : undefined;
|
const encryptedHeaders = headers ? infisicalSymmetricEncypt(JSON.stringify(headers)) : undefined;
|
||||||
const logStream = await auditLogStreamDAL.create({
|
const logStream = await auditLogStreamDAL.create({
|
||||||
|
@ -100,10 +100,10 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
// Filter by date range
|
// Filter by date range
|
||||||
if (startDate) {
|
if (startDate) {
|
||||||
void sqlQuery.where(`${TableName.AuditLog}.createdAt`, ">=", startDate);
|
void sqlQuery.whereRaw(`"${TableName.AuditLog}"."createdAt" >= ?::timestamptz`, [startDate]);
|
||||||
}
|
}
|
||||||
if (endDate) {
|
if (endDate) {
|
||||||
void sqlQuery.where(`${TableName.AuditLog}.createdAt`, "<=", endDate);
|
void sqlQuery.whereRaw(`"${TableName.AuditLog}"."createdAt" <= ?::timestamptz`, [endDate]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we timeout long running queries to prevent DB resource issues (2 minutes)
|
// we timeout long running queries to prevent DB resource issues (2 minutes)
|
||||||
|
@ -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 { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
|
||||||
@ -26,13 +27,14 @@ export const auditLogServiceFactory = ({
|
|||||||
const listAuditLogs = async ({ actorAuthMethod, actorId, actorOrgId, actor, filter }: TListProjectAuditLogDTO) => {
|
const listAuditLogs = async ({ actorAuthMethod, actorId, actorOrgId, actor, filter }: TListProjectAuditLogDTO) => {
|
||||||
// Filter logs for specific project
|
// Filter logs for specific project
|
||||||
if (filter.projectId) {
|
if (filter.projectId) {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
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 {
|
||||||
// Organization-wide logs
|
// Organization-wide logs
|
||||||
@ -79,7 +81,8 @@ export const auditLogServiceFactory = ({
|
|||||||
}
|
}
|
||||||
// add all cases in which project id or org id cannot be added
|
// add all cases in which project id or org id cannot be added
|
||||||
if (data.event.type !== EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH) {
|
if (data.event.type !== EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH) {
|
||||||
if (!data.projectId && !data.orgId) throw new BadRequestError({ message: "Must either project id or org id" });
|
if (!data.projectId && !data.orgId)
|
||||||
|
throw new BadRequestError({ message: "Must specify either project id or org id" });
|
||||||
}
|
}
|
||||||
|
|
||||||
return auditLogQueue.pushToLog(data);
|
return auditLogQueue.pushToLog(data);
|
||||||
|
@ -13,6 +13,13 @@ import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
|||||||
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-types";
|
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-types";
|
||||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||||
import { PkiItemType } from "@app/services/pki-collection/pki-collection-types";
|
import { PkiItemType } from "@app/services/pki-collection/pki-collection-types";
|
||||||
|
import { SecretSync, SecretSyncImportBehavior } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
import {
|
||||||
|
TCreateSecretSyncDTO,
|
||||||
|
TDeleteSecretSyncDTO,
|
||||||
|
TSecretSyncRaw,
|
||||||
|
TUpdateSecretSyncDTO
|
||||||
|
} from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
export type TListProjectAuditLogDTO = {
|
export type TListProjectAuditLogDTO = {
|
||||||
filter: {
|
filter: {
|
||||||
@ -31,7 +38,7 @@ export type TListProjectAuditLogDTO = {
|
|||||||
|
|
||||||
export type TCreateAuditLogDTO = {
|
export type TCreateAuditLogDTO = {
|
||||||
event: Event;
|
event: Event;
|
||||||
actor: UserActor | IdentityActor | ServiceActor | ScimClientActor | PlatformActor;
|
actor: UserActor | IdentityActor | ServiceActor | ScimClientActor | PlatformActor | UnknownUserActor;
|
||||||
orgId?: string;
|
orgId?: string;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
} & BaseAuthData;
|
} & BaseAuthData;
|
||||||
@ -226,10 +233,22 @@ export enum EventType {
|
|||||||
DELETE_PROJECT_TEMPLATE = "delete-project-template",
|
DELETE_PROJECT_TEMPLATE = "delete-project-template",
|
||||||
APPLY_PROJECT_TEMPLATE = "apply-project-template",
|
APPLY_PROJECT_TEMPLATE = "apply-project-template",
|
||||||
GET_APP_CONNECTIONS = "get-app-connections",
|
GET_APP_CONNECTIONS = "get-app-connections",
|
||||||
|
GET_AVAILABLE_APP_CONNECTIONS_DETAILS = "get-available-app-connections-details",
|
||||||
GET_APP_CONNECTION = "get-app-connection",
|
GET_APP_CONNECTION = "get-app-connection",
|
||||||
CREATE_APP_CONNECTION = "create-app-connection",
|
CREATE_APP_CONNECTION = "create-app-connection",
|
||||||
UPDATE_APP_CONNECTION = "update-app-connection",
|
UPDATE_APP_CONNECTION = "update-app-connection",
|
||||||
DELETE_APP_CONNECTION = "delete-app-connection"
|
DELETE_APP_CONNECTION = "delete-app-connection",
|
||||||
|
CREATE_SHARED_SECRET = "create-shared-secret",
|
||||||
|
DELETE_SHARED_SECRET = "delete-shared-secret",
|
||||||
|
READ_SHARED_SECRET = "read-shared-secret",
|
||||||
|
GET_SECRET_SYNCS = "get-secret-syncs",
|
||||||
|
GET_SECRET_SYNC = "get-secret-sync",
|
||||||
|
CREATE_SECRET_SYNC = "create-secret-sync",
|
||||||
|
UPDATE_SECRET_SYNC = "update-secret-sync",
|
||||||
|
DELETE_SECRET_SYNC = "delete-secret-sync",
|
||||||
|
SECRET_SYNC_SYNC_SECRETS = "secret-sync-sync-secrets",
|
||||||
|
SECRET_SYNC_IMPORT_SECRETS = "secret-sync-import-secrets",
|
||||||
|
SECRET_SYNC_REMOVE_SECRETS = "secret-sync-remove-secrets"
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UserActorMetadata {
|
interface UserActorMetadata {
|
||||||
@ -252,6 +271,8 @@ interface ScimClientActorMetadata {}
|
|||||||
|
|
||||||
interface PlatformActorMetadata {}
|
interface PlatformActorMetadata {}
|
||||||
|
|
||||||
|
interface UnknownUserActorMetadata {}
|
||||||
|
|
||||||
export interface UserActor {
|
export interface UserActor {
|
||||||
type: ActorType.USER;
|
type: ActorType.USER;
|
||||||
metadata: UserActorMetadata;
|
metadata: UserActorMetadata;
|
||||||
@ -267,6 +288,11 @@ export interface PlatformActor {
|
|||||||
metadata: PlatformActorMetadata;
|
metadata: PlatformActorMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UnknownUserActor {
|
||||||
|
type: ActorType.UNKNOWN_USER;
|
||||||
|
metadata: UnknownUserActorMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IdentityActor {
|
export interface IdentityActor {
|
||||||
type: ActorType.IDENTITY;
|
type: ActorType.IDENTITY;
|
||||||
metadata: IdentityActorMetadata;
|
metadata: IdentityActorMetadata;
|
||||||
@ -1883,6 +1909,15 @@ interface GetAppConnectionsEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GetAvailableAppConnectionsDetailsEvent {
|
||||||
|
type: EventType.GET_AVAILABLE_APP_CONNECTIONS_DETAILS;
|
||||||
|
metadata: {
|
||||||
|
app?: AppConnection;
|
||||||
|
count: number;
|
||||||
|
connectionIds: string[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface GetAppConnectionEvent {
|
interface GetAppConnectionEvent {
|
||||||
type: EventType.GET_APP_CONNECTION;
|
type: EventType.GET_APP_CONNECTION;
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -1907,6 +1942,107 @@ interface DeleteAppConnectionEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CreateSharedSecretEvent {
|
||||||
|
type: EventType.CREATE_SHARED_SECRET;
|
||||||
|
metadata: {
|
||||||
|
id: string;
|
||||||
|
accessType: string;
|
||||||
|
name?: string;
|
||||||
|
expiresAfterViews?: number;
|
||||||
|
usingPassword: boolean;
|
||||||
|
expiresAt: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeleteSharedSecretEvent {
|
||||||
|
type: EventType.DELETE_SHARED_SECRET;
|
||||||
|
metadata: {
|
||||||
|
id: string;
|
||||||
|
name?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReadSharedSecretEvent {
|
||||||
|
type: EventType.READ_SHARED_SECRET;
|
||||||
|
metadata: {
|
||||||
|
id: string;
|
||||||
|
name?: string;
|
||||||
|
accessType: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetSecretSyncsEvent {
|
||||||
|
type: EventType.GET_SECRET_SYNCS;
|
||||||
|
metadata: {
|
||||||
|
destination?: SecretSync;
|
||||||
|
count: number;
|
||||||
|
syncIds: string[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetSecretSyncEvent {
|
||||||
|
type: EventType.GET_SECRET_SYNC;
|
||||||
|
metadata: {
|
||||||
|
destination: SecretSync;
|
||||||
|
syncId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateSecretSyncEvent {
|
||||||
|
type: EventType.CREATE_SECRET_SYNC;
|
||||||
|
metadata: Omit<TCreateSecretSyncDTO, "projectId"> & { syncId: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateSecretSyncEvent {
|
||||||
|
type: EventType.UPDATE_SECRET_SYNC;
|
||||||
|
metadata: TUpdateSecretSyncDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeleteSecretSyncEvent {
|
||||||
|
type: EventType.DELETE_SECRET_SYNC;
|
||||||
|
metadata: TDeleteSecretSyncDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SecretSyncSyncSecretsEvent {
|
||||||
|
type: EventType.SECRET_SYNC_SYNC_SECRETS;
|
||||||
|
metadata: Pick<
|
||||||
|
TSecretSyncRaw,
|
||||||
|
"syncOptions" | "destinationConfig" | "destination" | "syncStatus" | "connectionId" | "folderId"
|
||||||
|
> & {
|
||||||
|
syncId: string;
|
||||||
|
syncMessage: string | null;
|
||||||
|
jobId: string;
|
||||||
|
jobRanAt: Date;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SecretSyncImportSecretsEvent {
|
||||||
|
type: EventType.SECRET_SYNC_IMPORT_SECRETS;
|
||||||
|
metadata: Pick<
|
||||||
|
TSecretSyncRaw,
|
||||||
|
"syncOptions" | "destinationConfig" | "destination" | "importStatus" | "connectionId" | "folderId"
|
||||||
|
> & {
|
||||||
|
syncId: string;
|
||||||
|
importMessage: string | null;
|
||||||
|
jobId: string;
|
||||||
|
jobRanAt: Date;
|
||||||
|
importBehavior: SecretSyncImportBehavior;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SecretSyncRemoveSecretsEvent {
|
||||||
|
type: EventType.SECRET_SYNC_REMOVE_SECRETS;
|
||||||
|
metadata: Pick<
|
||||||
|
TSecretSyncRaw,
|
||||||
|
"syncOptions" | "destinationConfig" | "destination" | "removeStatus" | "connectionId" | "folderId"
|
||||||
|
> & {
|
||||||
|
syncId: string;
|
||||||
|
removeMessage: string | null;
|
||||||
|
jobId: string;
|
||||||
|
jobRanAt: Date;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type Event =
|
export type Event =
|
||||||
| GetSecretsEvent
|
| GetSecretsEvent
|
||||||
| GetSecretEvent
|
| GetSecretEvent
|
||||||
@ -2080,7 +2216,19 @@ export type Event =
|
|||||||
| DeleteProjectTemplateEvent
|
| DeleteProjectTemplateEvent
|
||||||
| ApplyProjectTemplateEvent
|
| ApplyProjectTemplateEvent
|
||||||
| GetAppConnectionsEvent
|
| GetAppConnectionsEvent
|
||||||
|
| GetAvailableAppConnectionsDetailsEvent
|
||||||
| GetAppConnectionEvent
|
| GetAppConnectionEvent
|
||||||
| CreateAppConnectionEvent
|
| CreateAppConnectionEvent
|
||||||
| UpdateAppConnectionEvent
|
| UpdateAppConnectionEvent
|
||||||
| DeleteAppConnectionEvent;
|
| DeleteAppConnectionEvent
|
||||||
|
| CreateSharedSecretEvent
|
||||||
|
| DeleteSharedSecretEvent
|
||||||
|
| ReadSharedSecretEvent
|
||||||
|
| GetSecretSyncsEvent
|
||||||
|
| GetSecretSyncEvent
|
||||||
|
| CreateSecretSyncEvent
|
||||||
|
| UpdateSecretSyncEvent
|
||||||
|
| DeleteSecretSyncEvent
|
||||||
|
| SecretSyncSyncSecretsEvent
|
||||||
|
| SecretSyncImportSecretsEvent
|
||||||
|
| SecretSyncRemoveSecretsEvent;
|
||||||
|
@ -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";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
@ -66,13 +67,14 @@ export const certificateAuthorityCrlServiceFactory = ({
|
|||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
|
|
||||||
import { ProjectType, SecretKeyEncoding } from "@app/db/schemas";
|
import { ActionProjectType, SecretKeyEncoding } 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";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import {
|
import {
|
||||||
@ -67,14 +67,14 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
const projectId = project.id;
|
const projectId = project.id;
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionDynamicSecretActions.Lease,
|
ProjectPermissionDynamicSecretActions.Lease,
|
||||||
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
||||||
@ -147,14 +147,14 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
const projectId = project.id;
|
const projectId = project.id;
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionDynamicSecretActions.Lease,
|
ProjectPermissionDynamicSecretActions.Lease,
|
||||||
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
||||||
@ -227,14 +227,14 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
const projectId = project.id;
|
const projectId = project.id;
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionDynamicSecretActions.Lease,
|
ProjectPermissionDynamicSecretActions.Lease,
|
||||||
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
||||||
@ -297,13 +297,14 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
const projectId = project.id;
|
const projectId = project.id;
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionDynamicSecretActions.Lease,
|
ProjectPermissionDynamicSecretActions.Lease,
|
||||||
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
||||||
@ -339,13 +340,14 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
const projectId = project.id;
|
const projectId = project.id;
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionDynamicSecretActions.Lease,
|
ProjectPermissionDynamicSecretActions.Lease,
|
||||||
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
|
|
||||||
import { ProjectType, SecretKeyEncoding } from "@app/db/schemas";
|
import { ActionProjectType, SecretKeyEncoding } 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";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import {
|
import {
|
||||||
@ -73,14 +73,14 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
const projectId = project.id;
|
const projectId = project.id;
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionDynamicSecretActions.CreateRootCredential,
|
ProjectPermissionDynamicSecretActions.CreateRootCredential,
|
||||||
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
||||||
@ -145,14 +145,14 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
|
|
||||||
const projectId = project.id;
|
const projectId = project.id;
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionDynamicSecretActions.EditRootCredential,
|
ProjectPermissionDynamicSecretActions.EditRootCredential,
|
||||||
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
||||||
@ -229,14 +229,14 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
|
|
||||||
const projectId = project.id;
|
const projectId = project.id;
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionDynamicSecretActions.DeleteRootCredential,
|
ProjectPermissionDynamicSecretActions.DeleteRootCredential,
|
||||||
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
||||||
@ -290,13 +290,14 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
|
|
||||||
const projectId = project.id;
|
const projectId = project.id;
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionDynamicSecretActions.ReadRootCredential,
|
ProjectPermissionDynamicSecretActions.ReadRootCredential,
|
||||||
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
||||||
@ -340,13 +341,14 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
isInternal
|
isInternal
|
||||||
}: TListDynamicSecretsMultiEnvDTO) => {
|
}: TListDynamicSecretsMultiEnvDTO) => {
|
||||||
if (!isInternal) {
|
if (!isInternal) {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
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
|
||||||
environmentSlugs.forEach((environmentSlug) =>
|
environmentSlugs.forEach((environmentSlug) =>
|
||||||
@ -383,13 +385,14 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
search,
|
search,
|
||||||
projectId
|
projectId
|
||||||
}: TGetDynamicSecretsCountDTO) => {
|
}: TGetDynamicSecretsCountDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionDynamicSecretActions.ReadRootCredential,
|
ProjectPermissionDynamicSecretActions.ReadRootCredential,
|
||||||
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
||||||
@ -431,13 +434,14 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
projectId = project.id;
|
projectId = project.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionDynamicSecretActions.ReadRootCredential,
|
ProjectPermissionDynamicSecretActions.ReadRootCredential,
|
||||||
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path })
|
||||||
@ -462,13 +466,14 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
{ folderMappings, filters, projectId }: TListDynamicSecretsByFolderMappingsDTO,
|
{ folderMappings, filters, projectId }: TListDynamicSecretsByFolderMappingsDTO,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
) => {
|
) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
projectId,
|
projectId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
|
|
||||||
const userAccessibleFolderMappings = folderMappings.filter(({ path, environment }) =>
|
const userAccessibleFolderMappings = folderMappings.filter(({ path, environment }) =>
|
||||||
permission.can(
|
permission.can(
|
||||||
@ -507,13 +512,14 @@ export const dynamicSecretServiceFactory = ({
|
|||||||
...params
|
...params
|
||||||
}: TListDynamicSecretsMultiEnvDTO) => {
|
}: TListDynamicSecretsMultiEnvDTO) => {
|
||||||
if (!isInternal) {
|
if (!isInternal) {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
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
|
||||||
environmentSlugs.forEach((environmentSlug) =>
|
environmentSlugs.forEach((environmentSlug) =>
|
||||||
|
@ -34,6 +34,8 @@ export const SqlDatabaseProvider = (): TDynamicProviderFns => {
|
|||||||
|
|
||||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretSqlDBSchema>) => {
|
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretSqlDBSchema>) => {
|
||||||
const ssl = providerInputs.ca ? { rejectUnauthorized: false, ca: providerInputs.ca } : undefined;
|
const ssl = providerInputs.ca ? { rejectUnauthorized: false, ca: providerInputs.ca } : undefined;
|
||||||
|
const isMsSQLClient = providerInputs.client === SqlProviders.MsSQL;
|
||||||
|
|
||||||
const db = knex({
|
const db = knex({
|
||||||
client: providerInputs.client,
|
client: providerInputs.client,
|
||||||
connection: {
|
connection: {
|
||||||
@ -43,7 +45,16 @@ export const SqlDatabaseProvider = (): TDynamicProviderFns => {
|
|||||||
user: providerInputs.username,
|
user: providerInputs.username,
|
||||||
password: providerInputs.password,
|
password: providerInputs.password,
|
||||||
ssl,
|
ssl,
|
||||||
pool: { min: 0, max: 1 }
|
pool: { min: 0, max: 1 },
|
||||||
|
// @ts-expect-error this is because of knexjs type signature issue. This is directly passed to driver
|
||||||
|
// https://github.com/knex/knex/blob/b6507a7129d2b9fafebf5f831494431e64c6a8a0/lib/dialects/mssql/index.js#L66
|
||||||
|
// https://github.com/tediousjs/tedious/blob/ebb023ed90969a7ec0e4b036533ad52739d921f7/test/config.ci.ts#L19
|
||||||
|
options: isMsSQLClient
|
||||||
|
? {
|
||||||
|
trustServerCertificate: !providerInputs.ca,
|
||||||
|
cryptoCredentialsDetails: providerInputs.ca ? { ca: providerInputs.ca } : {}
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
},
|
},
|
||||||
acquireConnectionTimeout: EXTERNAL_REQUEST_TIMEOUT
|
acquireConnectionTimeout: EXTERNAL_REQUEST_TIMEOUT
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
import { KMSServiceException } from "@aws-sdk/client-kms";
|
||||||
|
import { STSServiceException } from "@aws-sdk/client-sts";
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
|
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
|
import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
@ -71,7 +73,16 @@ export const externalKmsServiceFactory = ({
|
|||||||
switch (provider.type) {
|
switch (provider.type) {
|
||||||
case KmsProviders.Aws:
|
case KmsProviders.Aws:
|
||||||
{
|
{
|
||||||
const externalKms = await AwsKmsProviderFactory({ inputs: provider.inputs });
|
const externalKms = await AwsKmsProviderFactory({ inputs: provider.inputs }).catch((error) => {
|
||||||
|
if (error instanceof STSServiceException || error instanceof KMSServiceException) {
|
||||||
|
throw new InternalServerError({
|
||||||
|
message: error.message ? `AWS error: ${error.message}` : ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
|
||||||
// if missing kms key this generate a new kms key id and returns new provider input
|
// if missing kms key this generate a new kms key id and returns new provider input
|
||||||
const newProviderInput = await externalKms.generateInputKmsKey();
|
const newProviderInput = await externalKms.generateInputKmsKey();
|
||||||
sanitizedProviderInput = JSON.stringify(newProviderInput);
|
sanitizedProviderInput = JSON.stringify(newProviderInput);
|
||||||
|
@ -32,7 +32,7 @@ type TGroupServiceFactoryDep = {
|
|||||||
userDAL: Pick<TUserDALFactory, "find" | "findUserEncKeyByUserIdsBatch" | "transaction" | "findOne">;
|
userDAL: Pick<TUserDALFactory, "find" | "findUserEncKeyByUserIdsBatch" | "transaction" | "findOne">;
|
||||||
groupDAL: Pick<
|
groupDAL: Pick<
|
||||||
TGroupDALFactory,
|
TGroupDALFactory,
|
||||||
"create" | "findOne" | "update" | "delete" | "findAllGroupPossibleMembers" | "findById"
|
"create" | "findOne" | "update" | "delete" | "findAllGroupPossibleMembers" | "findById" | "transaction"
|
||||||
>;
|
>;
|
||||||
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
|
||||||
orgDAL: Pick<TOrgDALFactory, "findMembership" | "countAllOrgMembers">;
|
orgDAL: Pick<TOrgDALFactory, "findMembership" | "countAllOrgMembers">;
|
||||||
@ -88,12 +88,26 @@ export const groupServiceFactory = ({
|
|||||||
if (!hasRequiredPriviledges)
|
if (!hasRequiredPriviledges)
|
||||||
throw new ForbiddenRequestError({ message: "Failed to create a more privileged group" });
|
throw new ForbiddenRequestError({ message: "Failed to create a more privileged group" });
|
||||||
|
|
||||||
const group = await groupDAL.create({
|
const group = await groupDAL.transaction(async (tx) => {
|
||||||
name,
|
const existingGroup = await groupDAL.findOne({ orgId: actorOrgId, name }, tx);
|
||||||
slug: slug || slugify(`${name}-${alphaNumericNanoId(4)}`),
|
if (existingGroup) {
|
||||||
orgId: actorOrgId,
|
throw new BadRequestError({
|
||||||
role: isCustomRole ? OrgMembershipRole.Custom : role,
|
message: `Failed to create group with name '${name}'. Group with the same name already exists`
|
||||||
roleId: customRole?.id
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const newGroup = await groupDAL.create(
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
slug: slug || slugify(`${name}-${alphaNumericNanoId(4)}`),
|
||||||
|
orgId: actorOrgId,
|
||||||
|
role: isCustomRole ? OrgMembershipRole.Custom : role,
|
||||||
|
roleId: customRole?.id
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
return newGroup;
|
||||||
});
|
});
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
@ -145,21 +159,36 @@ export const groupServiceFactory = ({
|
|||||||
if (isCustomRole) customRole = customOrgRole;
|
if (isCustomRole) customRole = customOrgRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [updatedGroup] = await groupDAL.update(
|
const updatedGroup = await groupDAL.transaction(async (tx) => {
|
||||||
{
|
if (name) {
|
||||||
id: group.id
|
const existingGroup = await groupDAL.findOne({ orgId: actorOrgId, name }, tx);
|
||||||
},
|
|
||||||
{
|
if (existingGroup && existingGroup.id !== id) {
|
||||||
name,
|
throw new BadRequestError({
|
||||||
slug: slug ? slugify(slug) : undefined,
|
message: `Failed to update group with name '${name}'. Group with the same name already exists`
|
||||||
...(role
|
});
|
||||||
? {
|
}
|
||||||
role: customRole ? OrgMembershipRole.Custom : role,
|
|
||||||
roleId: customRole?.id ?? null
|
|
||||||
}
|
|
||||||
: {})
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
const [updated] = await groupDAL.update(
|
||||||
|
{
|
||||||
|
id: group.id
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
slug: slug ? slugify(slug) : undefined,
|
||||||
|
...(role
|
||||||
|
? {
|
||||||
|
role: customRole ? OrgMembershipRole.Custom : role,
|
||||||
|
roleId: customRole?.id ?? null
|
||||||
|
}
|
||||||
|
: {})
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
});
|
||||||
|
|
||||||
return updatedGroup;
|
return updatedGroup;
|
||||||
};
|
};
|
||||||
|
@ -2,7 +2,7 @@ import { ForbiddenError, subject } from "@casl/ability";
|
|||||||
import { packRules } from "@casl/ability/extra";
|
import { packRules } from "@casl/ability/extra";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
|
|
||||||
import { TableName } from "@app/db/schemas";
|
import { ActionProjectType, TableName } from "@app/db/schemas";
|
||||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { unpackPermissions } from "@app/server/routes/santizedSchemas/permission";
|
import { unpackPermissions } from "@app/server/routes/santizedSchemas/permission";
|
||||||
@ -55,24 +55,26 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
if (!identityProjectMembership)
|
if (!identityProjectMembership)
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
);
|
);
|
||||||
const { permission: targetIdentityPermission } = await permissionService.getProjectPermission(
|
const { permission: targetIdentityPermission } = await permissionService.getProjectPermission({
|
||||||
ActorType.IDENTITY,
|
actor: ActorType.IDENTITY,
|
||||||
identityId,
|
actorId: identityId,
|
||||||
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
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
||||||
@ -135,24 +137,26 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
message: `Failed to find identity with membership ${identityPrivilege.projectMembershipId}`
|
message: `Failed to find identity with membership ${identityPrivilege.projectMembershipId}`
|
||||||
});
|
});
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
||||||
);
|
);
|
||||||
const { permission: targetIdentityPermission } = await permissionService.getProjectPermission(
|
const { permission: targetIdentityPermission } = await permissionService.getProjectPermission({
|
||||||
ActorType.IDENTITY,
|
actor: ActorType.IDENTITY,
|
||||||
identityProjectMembership.identityId,
|
actorId: identityProjectMembership.identityId,
|
||||||
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
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
||||||
@ -215,24 +219,26 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
message: `Failed to find identity with membership ${identityPrivilege.projectMembershipId}`
|
message: `Failed to find identity with membership ${identityPrivilege.projectMembershipId}`
|
||||||
});
|
});
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
||||||
);
|
);
|
||||||
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
|
const { permission: identityRolePermission } = await permissionService.getProjectPermission({
|
||||||
ActorType.IDENTITY,
|
actor: ActorType.IDENTITY,
|
||||||
identityProjectMembership.identityId,
|
actorId: identityProjectMembership.identityId,
|
||||||
identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
|
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
|
||||||
if (!hasRequiredPriviledges)
|
if (!hasRequiredPriviledges)
|
||||||
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
|
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
|
||||||
@ -260,13 +266,14 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
message: `Failed to find identity with membership ${identityPrivilege.projectMembershipId}`
|
message: `Failed to find identity with membership ${identityPrivilege.projectMembershipId}`
|
||||||
});
|
});
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
||||||
@ -294,13 +301,14 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
||||||
if (!identityProjectMembership)
|
if (!identityProjectMembership)
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
||||||
@ -329,13 +337,14 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
||||||
if (!identityProjectMembership)
|
if (!identityProjectMembership)
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
||||||
|
@ -2,6 +2,7 @@ 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 ms from "ms";
|
import ms from "ms";
|
||||||
|
|
||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { UnpackedPermissionSchema } from "@app/server/routes/santizedSchemas/permission";
|
import { UnpackedPermissionSchema } from "@app/server/routes/santizedSchemas/permission";
|
||||||
@ -62,25 +63,27 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
if (!identityProjectMembership)
|
if (!identityProjectMembership)
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
);
|
);
|
||||||
|
|
||||||
const { permission: targetIdentityPermission } = await permissionService.getProjectPermission(
|
const { permission: targetIdentityPermission } = await permissionService.getProjectPermission({
|
||||||
ActorType.IDENTITY,
|
actor: ActorType.IDENTITY,
|
||||||
identityId,
|
actorId: identityId,
|
||||||
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
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
||||||
@ -143,26 +146,28 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
if (!identityProjectMembership)
|
if (!identityProjectMembership)
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
);
|
);
|
||||||
|
|
||||||
const { permission: targetIdentityPermission } = await permissionService.getProjectPermission(
|
const { permission: targetIdentityPermission } = await permissionService.getProjectPermission({
|
||||||
ActorType.IDENTITY,
|
actor: ActorType.IDENTITY,
|
||||||
identityProjectMembership.identityId,
|
actorId: identityProjectMembership.identityId,
|
||||||
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
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
||||||
@ -242,25 +247,27 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
if (!identityProjectMembership)
|
if (!identityProjectMembership)
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
);
|
);
|
||||||
|
|
||||||
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
|
const { permission: identityRolePermission } = await permissionService.getProjectPermission({
|
||||||
ActorType.IDENTITY,
|
actor: ActorType.IDENTITY,
|
||||||
identityProjectMembership.identityId,
|
actorId: identityProjectMembership.identityId,
|
||||||
identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
|
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
|
||||||
if (!hasRequiredPriviledges)
|
if (!hasRequiredPriviledges)
|
||||||
throw new ForbiddenRequestError({ message: "Failed to edit more privileged identity" });
|
throw new ForbiddenRequestError({ message: "Failed to edit more privileged identity" });
|
||||||
@ -299,13 +306,14 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
||||||
if (!identityProjectMembership)
|
if (!identityProjectMembership)
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
@ -341,13 +349,14 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
||||||
if (!identityProjectMembership)
|
if (!identityProjectMembership)
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
|
@ -476,14 +476,14 @@ export const ldapConfigServiceFactory = ({
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const plan = await licenseService.getPlan(orgId);
|
const plan = await licenseService.getPlan(orgId);
|
||||||
if (plan?.memberLimit && plan.membersUsed >= plan.memberLimit) {
|
if (plan?.slug !== "enterprise" && plan?.memberLimit && plan.membersUsed >= plan.memberLimit) {
|
||||||
// limit imposed on number of members allowed / number of members used exceeds the number of members allowed
|
// limit imposed on number of members allowed / number of members used exceeds the number of members allowed
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Failed to create new member via LDAP due to member limit reached. Upgrade plan to add more members."
|
message: "Failed to create new member via LDAP due to member limit reached. Upgrade plan to add more members."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plan?.identityLimit && plan.identitiesUsed >= plan.identityLimit) {
|
if (plan?.slug !== "enterprise" && plan?.identityLimit && plan.identitiesUsed >= plan.identityLimit) {
|
||||||
// limit imposed on number of identities allowed / number of identities used exceeds the number of identities allowed
|
// limit imposed on number of identities allowed / number of identities used exceeds the number of identities allowed
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Failed to create new member via LDAP due to member limit reached. Upgrade plan to add more members."
|
message: "Failed to create new member via LDAP due to member limit reached. Upgrade plan to add more members."
|
||||||
|
@ -50,8 +50,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
|||||||
},
|
},
|
||||||
pkiEst: false,
|
pkiEst: false,
|
||||||
enforceMfa: false,
|
enforceMfa: false,
|
||||||
projectTemplates: false,
|
projectTemplates: false
|
||||||
appConnections: false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {
|
export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {
|
||||||
|
@ -68,7 +68,6 @@ export type TFeatureSet = {
|
|||||||
pkiEst: boolean;
|
pkiEst: boolean;
|
||||||
enforceMfa: boolean;
|
enforceMfa: boolean;
|
||||||
projectTemplates: false;
|
projectTemplates: false;
|
||||||
appConnections: false; // TODO: remove once live
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TOrgPlansTableDTO = {
|
export type TOrgPlansTableDTO = {
|
||||||
|
@ -1,4 +1,12 @@
|
|||||||
import { AbilityBuilder, createMongoAbility, MongoAbility } from "@casl/ability";
|
import { AbilityBuilder, createMongoAbility, ForcedSubject, MongoAbility } from "@casl/ability";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import {
|
||||||
|
CASL_ACTION_SCHEMA_ENUM,
|
||||||
|
CASL_ACTION_SCHEMA_NATIVE_ENUM
|
||||||
|
} from "@app/ee/services/permission/permission-schemas";
|
||||||
|
import { PermissionConditionSchema } from "@app/ee/services/permission/permission-types";
|
||||||
|
import { PermissionConditionOperators } from "@app/lib/casl";
|
||||||
|
|
||||||
export enum OrgPermissionActions {
|
export enum OrgPermissionActions {
|
||||||
Read = "read",
|
Read = "read",
|
||||||
@ -7,6 +15,14 @@ export enum OrgPermissionActions {
|
|||||||
Delete = "delete"
|
Delete = "delete"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum OrgPermissionAppConnectionActions {
|
||||||
|
Read = "read",
|
||||||
|
Create = "create",
|
||||||
|
Edit = "edit",
|
||||||
|
Delete = "delete",
|
||||||
|
Connect = "connect"
|
||||||
|
}
|
||||||
|
|
||||||
export enum OrgPermissionAdminConsoleAction {
|
export enum OrgPermissionAdminConsoleAction {
|
||||||
AccessAllProjects = "access-all-projects"
|
AccessAllProjects = "access-all-projects"
|
||||||
}
|
}
|
||||||
@ -31,6 +47,10 @@ export enum OrgPermissionSubjects {
|
|||||||
AppConnections = "app-connections"
|
AppConnections = "app-connections"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type AppConnectionSubjectFields = {
|
||||||
|
connectionId: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type OrgPermissionSet =
|
export type OrgPermissionSet =
|
||||||
| [OrgPermissionActions.Create, OrgPermissionSubjects.Workspace]
|
| [OrgPermissionActions.Create, OrgPermissionSubjects.Workspace]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.Role]
|
| [OrgPermissionActions, OrgPermissionSubjects.Role]
|
||||||
@ -47,9 +67,109 @@ export type OrgPermissionSet =
|
|||||||
| [OrgPermissionActions, OrgPermissionSubjects.Kms]
|
| [OrgPermissionActions, OrgPermissionSubjects.Kms]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.AuditLogs]
|
| [OrgPermissionActions, OrgPermissionSubjects.AuditLogs]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates]
|
| [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.AppConnections]
|
| [
|
||||||
|
OrgPermissionAppConnectionActions,
|
||||||
|
(
|
||||||
|
| OrgPermissionSubjects.AppConnections
|
||||||
|
| (ForcedSubject<OrgPermissionSubjects.AppConnections> & AppConnectionSubjectFields)
|
||||||
|
)
|
||||||
|
]
|
||||||
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole];
|
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole];
|
||||||
|
|
||||||
|
const AppConnectionConditionSchema = z
|
||||||
|
.object({
|
||||||
|
connectionId: z.union([
|
||||||
|
z.string(),
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
|
||||||
|
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
|
||||||
|
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN]
|
||||||
|
})
|
||||||
|
.partial()
|
||||||
|
])
|
||||||
|
})
|
||||||
|
.partial();
|
||||||
|
|
||||||
|
export const OrgPermissionSchema = z.discriminatedUnion("subject", [
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.Workspace).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_ENUM([OrgPermissionActions.Create]).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.Role).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.Member).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.Settings).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.IncidentAccount).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.Sso).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.Scim).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.Ldap).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.Groups).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.SecretScanning).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.Billing).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.Identity).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.Kms).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.AuditLogs).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.ProjectTemplates).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionActions).describe("Describe what action an entity can take.")
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.AppConnections).describe("The entity this permission pertains to."),
|
||||||
|
inverted: z.boolean().optional().describe("Whether rule allows or forbids."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionAppConnectionActions).describe(
|
||||||
|
"Describe what action an entity can take."
|
||||||
|
),
|
||||||
|
conditions: AppConnectionConditionSchema.describe(
|
||||||
|
"When specified, only matching conditions will be allowed to access given resource."
|
||||||
|
).optional()
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.AdminConsole).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionAdminConsoleAction).describe(
|
||||||
|
"Describe what action an entity can take."
|
||||||
|
)
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
const buildAdminPermission = () => {
|
const buildAdminPermission = () => {
|
||||||
const { can, rules } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility);
|
const { can, rules } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility);
|
||||||
// ws permissions
|
// ws permissions
|
||||||
@ -125,10 +245,11 @@ const buildAdminPermission = () => {
|
|||||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.ProjectTemplates);
|
can(OrgPermissionActions.Edit, OrgPermissionSubjects.ProjectTemplates);
|
||||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.ProjectTemplates);
|
can(OrgPermissionActions.Delete, OrgPermissionSubjects.ProjectTemplates);
|
||||||
|
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.AppConnections);
|
can(OrgPermissionAppConnectionActions.Read, OrgPermissionSubjects.AppConnections);
|
||||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.AppConnections);
|
can(OrgPermissionAppConnectionActions.Create, OrgPermissionSubjects.AppConnections);
|
||||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.AppConnections);
|
can(OrgPermissionAppConnectionActions.Edit, OrgPermissionSubjects.AppConnections);
|
||||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.AppConnections);
|
can(OrgPermissionAppConnectionActions.Delete, OrgPermissionSubjects.AppConnections);
|
||||||
|
can(OrgPermissionAppConnectionActions.Connect, OrgPermissionSubjects.AppConnections);
|
||||||
|
|
||||||
can(OrgPermissionAdminConsoleAction.AccessAllProjects, OrgPermissionSubjects.AdminConsole);
|
can(OrgPermissionAdminConsoleAction.AccessAllProjects, OrgPermissionSubjects.AdminConsole);
|
||||||
|
|
||||||
@ -160,7 +281,7 @@ const buildMemberPermission = () => {
|
|||||||
|
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.AuditLogs);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.AuditLogs);
|
||||||
|
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.AppConnections);
|
can(OrgPermissionAppConnectionActions.Connect, OrgPermissionSubjects.AppConnections);
|
||||||
|
|
||||||
return rules;
|
return rules;
|
||||||
};
|
};
|
||||||
|
9
backend/src/ee/services/permission/permission-schemas.ts
Normal file
9
backend/src/ee/services/permission/permission-schemas.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const CASL_ACTION_SCHEMA_NATIVE_ENUM = <ACTION extends z.EnumLike>(actions: ACTION) =>
|
||||||
|
z
|
||||||
|
.union([z.nativeEnum(actions), z.nativeEnum(actions).array().min(1)])
|
||||||
|
.transform((el) => (typeof el === "string" ? [el] : el));
|
||||||
|
|
||||||
|
export const CASL_ACTION_SCHEMA_ENUM = <ACTION extends z.EnumValues>(actions: ACTION) =>
|
||||||
|
z.union([z.enum(actions), z.enum(actions).array().min(1)]).transform((el) => (typeof el === "string" ? [el] : el));
|
@ -1,3 +1,6 @@
|
|||||||
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
|
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export type TBuildProjectPermissionDTO = {
|
export type TBuildProjectPermissionDTO = {
|
||||||
permissions?: unknown;
|
permissions?: unknown;
|
||||||
role: string;
|
role: string;
|
||||||
@ -7,3 +10,34 @@ export type TBuildOrgPermissionDTO = {
|
|||||||
permissions?: unknown;
|
permissions?: unknown;
|
||||||
role: string;
|
role: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
|
export type TGetUserProjectPermissionArg = {
|
||||||
|
userId: string;
|
||||||
|
projectId: string;
|
||||||
|
authMethod: ActorAuthMethod;
|
||||||
|
actionProjectType: ActionProjectType;
|
||||||
|
userOrgId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TGetIdentityProjectPermissionArg = {
|
||||||
|
identityId: string;
|
||||||
|
projectId: string;
|
||||||
|
identityOrgId?: string;
|
||||||
|
actionProjectType: ActionProjectType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TGetServiceTokenProjectPermissionArg = {
|
||||||
|
serviceTokenId: string;
|
||||||
|
projectId: string;
|
||||||
|
actorOrgId?: string;
|
||||||
|
actionProjectType: ActionProjectType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TGetProjectPermissionArg = {
|
||||||
|
actor: ActorType;
|
||||||
|
actorId: string;
|
||||||
|
projectId: string;
|
||||||
|
actorAuthMethod: ActorAuthMethod;
|
||||||
|
actorOrgId?: string;
|
||||||
|
actionProjectType: ActionProjectType;
|
||||||
|
};
|
||||||
|
@ -4,9 +4,9 @@ import { MongoQuery } from "@ucast/mongo2js";
|
|||||||
import handlebars from "handlebars";
|
import handlebars from "handlebars";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ActionProjectType,
|
||||||
OrgMembershipRole,
|
OrgMembershipRole,
|
||||||
ProjectMembershipRole,
|
ProjectMembershipRole,
|
||||||
ProjectType,
|
|
||||||
ServiceTokenScopes,
|
ServiceTokenScopes,
|
||||||
TIdentityProjectMemberships,
|
TIdentityProjectMemberships,
|
||||||
TProjectMemberships
|
TProjectMemberships
|
||||||
@ -23,7 +23,14 @@ import { TServiceTokenDALFactory } from "@app/services/service-token/service-tok
|
|||||||
import { orgAdminPermissions, orgMemberPermissions, orgNoAccessPermissions, OrgPermissionSet } from "./org-permission";
|
import { orgAdminPermissions, orgMemberPermissions, orgNoAccessPermissions, OrgPermissionSet } from "./org-permission";
|
||||||
import { TPermissionDALFactory } from "./permission-dal";
|
import { TPermissionDALFactory } from "./permission-dal";
|
||||||
import { escapeHandlebarsMissingMetadata, validateOrgSSO } from "./permission-fns";
|
import { escapeHandlebarsMissingMetadata, validateOrgSSO } from "./permission-fns";
|
||||||
import { TBuildOrgPermissionDTO, TBuildProjectPermissionDTO } from "./permission-service-types";
|
import {
|
||||||
|
TBuildOrgPermissionDTO,
|
||||||
|
TBuildProjectPermissionDTO,
|
||||||
|
TGetIdentityProjectPermissionArg,
|
||||||
|
TGetProjectPermissionArg,
|
||||||
|
TGetServiceTokenProjectPermissionArg,
|
||||||
|
TGetUserProjectPermissionArg
|
||||||
|
} from "./permission-service-types";
|
||||||
import {
|
import {
|
||||||
buildServiceTokenProjectPermission,
|
buildServiceTokenProjectPermission,
|
||||||
projectAdminPermissions,
|
projectAdminPermissions,
|
||||||
@ -193,12 +200,13 @@ export const permissionServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// user permission for a project in an organization
|
// user permission for a project in an organization
|
||||||
const getUserProjectPermission = async (
|
const getUserProjectPermission = async ({
|
||||||
userId: string,
|
userId,
|
||||||
projectId: string,
|
projectId,
|
||||||
authMethod: ActorAuthMethod,
|
authMethod,
|
||||||
userOrgId?: string
|
userOrgId,
|
||||||
): Promise<TProjectPermissionRT<ActorType.USER>> => {
|
actionProjectType
|
||||||
|
}: 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" });
|
||||||
|
|
||||||
@ -219,6 +227,12 @@ export const permissionServiceFactory = ({
|
|||||||
|
|
||||||
validateOrgSSO(authMethod, userProjectPermission.orgAuthEnforced);
|
validateOrgSSO(authMethod, userProjectPermission.orgAuthEnforced);
|
||||||
|
|
||||||
|
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 =
|
||||||
@ -256,13 +270,6 @@ export const permissionServiceFactory = ({
|
|||||||
return {
|
return {
|
||||||
permission,
|
permission,
|
||||||
membership: userProjectPermission,
|
membership: userProjectPermission,
|
||||||
ForbidOnInvalidProjectType: (productType: ProjectType) => {
|
|
||||||
if (productType !== userProjectPermission.projectType) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: `The project is of type ${userProjectPermission.projectType}. Operations of type ${productType} are not allowed.`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hasRole: (role: string) =>
|
hasRole: (role: string) =>
|
||||||
userProjectPermission.roles.findIndex(
|
userProjectPermission.roles.findIndex(
|
||||||
({ role: slug, customRoleSlug }) => role === slug || slug === customRoleSlug
|
({ role: slug, customRoleSlug }) => role === slug || slug === customRoleSlug
|
||||||
@ -270,11 +277,12 @@ export const permissionServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getIdentityProjectPermission = async (
|
const getIdentityProjectPermission = async ({
|
||||||
identityId: string,
|
identityId,
|
||||||
projectId: string,
|
projectId,
|
||||||
identityOrgId: string | undefined
|
identityOrgId,
|
||||||
): Promise<TProjectPermissionRT<ActorType.IDENTITY>> => {
|
actionProjectType
|
||||||
|
}: TGetIdentityProjectPermissionArg): Promise<TProjectPermissionRT<ActorType.IDENTITY>> => {
|
||||||
const identityProjectPermission = await permissionDAL.getProjectIdentityPermission(identityId, projectId);
|
const identityProjectPermission = await permissionDAL.getProjectIdentityPermission(identityId, projectId);
|
||||||
if (!identityProjectPermission)
|
if (!identityProjectPermission)
|
||||||
throw new ForbiddenRequestError({
|
throw new ForbiddenRequestError({
|
||||||
@ -293,6 +301,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 =
|
||||||
@ -331,13 +345,6 @@ export const permissionServiceFactory = ({
|
|||||||
return {
|
return {
|
||||||
permission,
|
permission,
|
||||||
membership: identityProjectPermission,
|
membership: identityProjectPermission,
|
||||||
ForbidOnInvalidProjectType: (productType: ProjectType) => {
|
|
||||||
if (productType !== identityProjectPermission.projectType) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: `The project is of type ${identityProjectPermission.projectType}. Operations of type ${productType} are not allowed.`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hasRole: (role: string) =>
|
hasRole: (role: string) =>
|
||||||
identityProjectPermission.roles.findIndex(
|
identityProjectPermission.roles.findIndex(
|
||||||
({ role: slug, customRoleSlug }) => role === slug || slug === customRoleSlug
|
({ role: slug, customRoleSlug }) => role === slug || slug === customRoleSlug
|
||||||
@ -345,11 +352,12 @@ export const permissionServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getServiceTokenProjectPermission = async (
|
const getServiceTokenProjectPermission = async ({
|
||||||
serviceTokenId: string,
|
serviceTokenId,
|
||||||
projectId: string,
|
projectId,
|
||||||
actorOrgId: string | undefined
|
actorOrgId,
|
||||||
) => {
|
actionProjectType
|
||||||
|
}: 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` });
|
||||||
|
|
||||||
@ -373,17 +381,16 @@ 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),
|
||||||
membership: undefined,
|
membership: undefined
|
||||||
ForbidOnInvalidProjectType: (productType: ProjectType) => {
|
|
||||||
if (productType !== serviceTokenProject.type) {
|
|
||||||
throw new BadRequestError({
|
|
||||||
message: `The project is of type ${serviceTokenProject.type}. Operations of type ${productType} are not allowed.`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -392,7 +399,6 @@ export const permissionServiceFactory = ({
|
|||||||
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
||||||
membership: undefined;
|
membership: undefined;
|
||||||
hasRole: (arg: string) => boolean;
|
hasRole: (arg: string) => boolean;
|
||||||
ForbidOnInvalidProjectType: (type: ProjectType) => void;
|
|
||||||
} // service token doesn't have both membership and roles
|
} // service token doesn't have both membership and roles
|
||||||
: {
|
: {
|
||||||
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
|
||||||
@ -402,7 +408,6 @@ export const permissionServiceFactory = ({
|
|||||||
roles: Array<{ role: string }>;
|
roles: Array<{ role: string }>;
|
||||||
};
|
};
|
||||||
hasRole: (role: string) => boolean;
|
hasRole: (role: string) => boolean;
|
||||||
ForbidOnInvalidProjectType: (type: ProjectType) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProjectPermissions = async (projectId: string) => {
|
const getProjectPermissions = async (projectId: string) => {
|
||||||
@ -522,20 +527,37 @@ export const permissionServiceFactory = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProjectPermission = async <T extends ActorType>(
|
const getProjectPermission = async <T extends ActorType>({
|
||||||
type: T,
|
actor,
|
||||||
id: string,
|
actorId,
|
||||||
projectId: string,
|
projectId,
|
||||||
actorAuthMethod: ActorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId: string | undefined
|
actorOrgId,
|
||||||
): Promise<TProjectPermissionRT<T>> => {
|
actionProjectType
|
||||||
switch (type) {
|
}: TGetProjectPermissionArg): Promise<TProjectPermissionRT<T>> => {
|
||||||
|
switch (actor) {
|
||||||
case ActorType.USER:
|
case ActorType.USER:
|
||||||
return getUserProjectPermission(id, projectId, actorAuthMethod, actorOrgId) as Promise<TProjectPermissionRT<T>>;
|
return getUserProjectPermission({
|
||||||
|
userId: actorId,
|
||||||
|
projectId,
|
||||||
|
authMethod: actorAuthMethod,
|
||||||
|
userOrgId: actorOrgId,
|
||||||
|
actionProjectType
|
||||||
|
}) as Promise<TProjectPermissionRT<T>>;
|
||||||
case ActorType.SERVICE:
|
case ActorType.SERVICE:
|
||||||
return getServiceTokenProjectPermission(id, projectId, actorOrgId) as Promise<TProjectPermissionRT<T>>;
|
return getServiceTokenProjectPermission({
|
||||||
|
serviceTokenId: actorId,
|
||||||
|
projectId,
|
||||||
|
actorOrgId,
|
||||||
|
actionProjectType
|
||||||
|
}) as Promise<TProjectPermissionRT<T>>;
|
||||||
case ActorType.IDENTITY:
|
case ActorType.IDENTITY:
|
||||||
return getIdentityProjectPermission(id, projectId, actorOrgId) as Promise<TProjectPermissionRT<T>>;
|
return getIdentityProjectPermission({
|
||||||
|
identityId: actorId,
|
||||||
|
projectId,
|
||||||
|
identityOrgId: actorOrgId,
|
||||||
|
actionProjectType
|
||||||
|
}) as Promise<TProjectPermissionRT<T>>;
|
||||||
default:
|
default:
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Invalid actor provided",
|
message: "Invalid actor provided",
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import { AbilityBuilder, createMongoAbility, ForcedSubject, MongoAbility } from "@casl/ability";
|
import { AbilityBuilder, createMongoAbility, ForcedSubject, MongoAbility } from "@casl/ability";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import {
|
||||||
|
CASL_ACTION_SCHEMA_ENUM,
|
||||||
|
CASL_ACTION_SCHEMA_NATIVE_ENUM
|
||||||
|
} from "@app/ee/services/permission/permission-schemas";
|
||||||
import { conditionsMatcher, PermissionConditionOperators } from "@app/lib/casl";
|
import { conditionsMatcher, PermissionConditionOperators } from "@app/lib/casl";
|
||||||
import { UnpackedPermissionSchema } from "@app/server/routes/santizedSchemas/permission";
|
import { UnpackedPermissionSchema } from "@app/server/routes/santizedSchemas/permission";
|
||||||
|
|
||||||
@ -30,6 +34,16 @@ export enum ProjectPermissionDynamicSecretActions {
|
|||||||
Lease = "lease"
|
Lease = "lease"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ProjectPermissionSecretSyncActions {
|
||||||
|
Read = "read",
|
||||||
|
Create = "create",
|
||||||
|
Edit = "edit",
|
||||||
|
Delete = "delete",
|
||||||
|
SyncSecrets = "sync-secrets",
|
||||||
|
ImportSecrets = "import-secrets",
|
||||||
|
RemoveSecrets = "remove-secrets"
|
||||||
|
}
|
||||||
|
|
||||||
export enum ProjectPermissionSub {
|
export enum ProjectPermissionSub {
|
||||||
Role = "role",
|
Role = "role",
|
||||||
Member = "member",
|
Member = "member",
|
||||||
@ -60,7 +74,8 @@ export enum ProjectPermissionSub {
|
|||||||
PkiAlerts = "pki-alerts",
|
PkiAlerts = "pki-alerts",
|
||||||
PkiCollections = "pki-collections",
|
PkiCollections = "pki-collections",
|
||||||
Kms = "kms",
|
Kms = "kms",
|
||||||
Cmek = "cmek"
|
Cmek = "cmek",
|
||||||
|
SecretSyncs = "secret-syncs"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SecretSubjectFields = {
|
export type SecretSubjectFields = {
|
||||||
@ -140,6 +155,7 @@ export type ProjectPermissionSet =
|
|||||||
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificateTemplates]
|
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificateTemplates]
|
||||||
| [ProjectPermissionActions, ProjectPermissionSub.PkiAlerts]
|
| [ProjectPermissionActions, ProjectPermissionSub.PkiAlerts]
|
||||||
| [ProjectPermissionActions, ProjectPermissionSub.PkiCollections]
|
| [ProjectPermissionActions, ProjectPermissionSub.PkiCollections]
|
||||||
|
| [ProjectPermissionSecretSyncActions, ProjectPermissionSub.SecretSyncs]
|
||||||
| [ProjectPermissionCmekActions, ProjectPermissionSub.Cmek]
|
| [ProjectPermissionCmekActions, ProjectPermissionSub.Cmek]
|
||||||
| [ProjectPermissionActions.Delete, ProjectPermissionSub.Project]
|
| [ProjectPermissionActions.Delete, ProjectPermissionSub.Project]
|
||||||
| [ProjectPermissionActions.Edit, ProjectPermissionSub.Project]
|
| [ProjectPermissionActions.Edit, ProjectPermissionSub.Project]
|
||||||
@ -147,14 +163,6 @@ export type ProjectPermissionSet =
|
|||||||
| [ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback]
|
| [ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback]
|
||||||
| [ProjectPermissionActions.Edit, ProjectPermissionSub.Kms];
|
| [ProjectPermissionActions.Edit, ProjectPermissionSub.Kms];
|
||||||
|
|
||||||
const CASL_ACTION_SCHEMA_NATIVE_ENUM = <ACTION extends z.EnumLike>(actions: ACTION) =>
|
|
||||||
z
|
|
||||||
.union([z.nativeEnum(actions), z.nativeEnum(actions).array().min(1)])
|
|
||||||
.transform((el) => (typeof el === "string" ? [el] : el));
|
|
||||||
|
|
||||||
const CASL_ACTION_SCHEMA_ENUM = <ACTION extends z.EnumValues>(actions: ACTION) =>
|
|
||||||
z.union([z.enum(actions), z.enum(actions).array().min(1)]).transform((el) => (typeof el === "string" ? [el] : el));
|
|
||||||
|
|
||||||
// akhilmhdh: don't modify this for v2
|
// akhilmhdh: don't modify this for v2
|
||||||
// if you want to update create a new schema
|
// if you want to update create a new schema
|
||||||
const SecretConditionV1Schema = z
|
const SecretConditionV1Schema = z
|
||||||
@ -392,10 +400,15 @@ const GeneralPermissionSchema = [
|
|||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
subject: z.literal(ProjectPermissionSub.Cmek).describe("The entity this permission pertains to."),
|
subject: z.literal(ProjectPermissionSub.Cmek).describe("The entity this permission pertains to."),
|
||||||
inverted: z.boolean().optional().describe("Whether rule allows or forbids."),
|
|
||||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionCmekActions).describe(
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionCmekActions).describe(
|
||||||
"Describe what action an entity can take."
|
"Describe what action an entity can take."
|
||||||
)
|
)
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(ProjectPermissionSub.SecretSyncs).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionSecretSyncActions).describe(
|
||||||
|
"Describe what action an entity can take."
|
||||||
|
)
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -549,6 +562,18 @@ const buildAdminPermissionRules = () => {
|
|||||||
],
|
],
|
||||||
ProjectPermissionSub.Cmek
|
ProjectPermissionSub.Cmek
|
||||||
);
|
);
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionSecretSyncActions.Create,
|
||||||
|
ProjectPermissionSecretSyncActions.Edit,
|
||||||
|
ProjectPermissionSecretSyncActions.Delete,
|
||||||
|
ProjectPermissionSecretSyncActions.Read,
|
||||||
|
ProjectPermissionSecretSyncActions.SyncSecrets,
|
||||||
|
ProjectPermissionSecretSyncActions.ImportSecrets,
|
||||||
|
ProjectPermissionSecretSyncActions.RemoveSecrets
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.SecretSyncs
|
||||||
|
);
|
||||||
return rules;
|
return rules;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -713,6 +738,19 @@ const buildMemberPermissionRules = () => {
|
|||||||
ProjectPermissionSub.Cmek
|
ProjectPermissionSub.Cmek
|
||||||
);
|
);
|
||||||
|
|
||||||
|
can(
|
||||||
|
[
|
||||||
|
ProjectPermissionSecretSyncActions.Create,
|
||||||
|
ProjectPermissionSecretSyncActions.Edit,
|
||||||
|
ProjectPermissionSecretSyncActions.Delete,
|
||||||
|
ProjectPermissionSecretSyncActions.Read,
|
||||||
|
ProjectPermissionSecretSyncActions.SyncSecrets,
|
||||||
|
ProjectPermissionSecretSyncActions.ImportSecrets,
|
||||||
|
ProjectPermissionSecretSyncActions.RemoveSecrets
|
||||||
|
],
|
||||||
|
ProjectPermissionSub.SecretSyncs
|
||||||
|
);
|
||||||
|
|
||||||
return rules;
|
return rules;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -746,6 +784,7 @@ const buildViewerPermissionRules = () => {
|
|||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateAuthorities);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateAuthorities);
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates);
|
||||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateTemplates);
|
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateTemplates);
|
||||||
|
can(ProjectPermissionSecretSyncActions.Read, ProjectPermissionSub.SecretSyncs);
|
||||||
|
|
||||||
return rules;
|
return rules;
|
||||||
};
|
};
|
||||||
|
@ -2,7 +2,7 @@ import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
|
|||||||
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
|
|
||||||
import { TableName } from "@app/db/schemas";
|
import { ActionProjectType, TableName } from "@app/db/schemas";
|
||||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { UnpackedPermissionSchema } from "@app/server/routes/santizedSchemas/permission";
|
import { UnpackedPermissionSchema } from "@app/server/routes/santizedSchemas/permission";
|
||||||
@ -55,21 +55,23 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
if (!projectMembership)
|
if (!projectMembership)
|
||||||
throw new NotFoundError({ message: `Project membership with ID ${projectMembershipId} found` });
|
throw new NotFoundError({ message: `Project membership with ID ${projectMembershipId} found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectMembership.projectId,
|
projectId: projectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
|
||||||
const { permission: targetUserPermission } = await permissionService.getProjectPermission(
|
const { permission: targetUserPermission } = await permissionService.getProjectPermission({
|
||||||
ActorType.USER,
|
actor: ActorType.USER,
|
||||||
projectMembership.userId,
|
actorId: projectMembership.userId,
|
||||||
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
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
||||||
@ -140,21 +142,23 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
message: `Project membership for user with ID '${userPrivilege.userId}' not found in project with ID '${userPrivilege.projectId}'`
|
message: `Project membership for user with ID '${userPrivilege.userId}' not found in project with ID '${userPrivilege.projectId}'`
|
||||||
});
|
});
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectMembership.projectId,
|
projectId: projectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
|
||||||
const { permission: targetUserPermission } = await permissionService.getProjectPermission(
|
const { permission: targetUserPermission } = await permissionService.getProjectPermission({
|
||||||
ActorType.USER,
|
actor: ActorType.USER,
|
||||||
projectMembership.userId,
|
actorId: projectMembership.userId,
|
||||||
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
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
||||||
@ -224,13 +228,14 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
message: `Project membership for user with ID '${userPrivilege.userId}' not found in project with ID '${userPrivilege.projectId}'`
|
message: `Project membership for user with ID '${userPrivilege.userId}' not found in project with ID '${userPrivilege.projectId}'`
|
||||||
});
|
});
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectMembership.projectId,
|
projectId: projectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member);
|
||||||
|
|
||||||
const deletedPrivilege = await projectUserAdditionalPrivilegeDAL.deleteById(userPrivilege.id);
|
const deletedPrivilege = await projectUserAdditionalPrivilegeDAL.deleteById(userPrivilege.id);
|
||||||
@ -260,13 +265,14 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
message: `Project membership for user with ID '${userPrivilege.userId}' not found in project with ID '${userPrivilege.projectId}'`
|
message: `Project membership for user with ID '${userPrivilege.userId}' not found in project with ID '${userPrivilege.projectId}'`
|
||||||
});
|
});
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectMembership.projectId,
|
projectId: projectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -286,13 +292,14 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
|
|||||||
if (!projectMembership)
|
if (!projectMembership)
|
||||||
throw new NotFoundError({ message: `Project membership with ID ${projectMembershipId} not found` });
|
throw new NotFoundError({ message: `Project membership with ID ${projectMembershipId} not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectMembership.projectId,
|
projectId: projectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
|
||||||
|
|
||||||
const userPrivileges = await projectUserAdditionalPrivilegeDAL.find(
|
const userPrivileges = await projectUserAdditionalPrivilegeDAL.find(
|
||||||
|
@ -421,14 +421,14 @@ export const samlConfigServiceFactory = ({
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const plan = await licenseService.getPlan(orgId);
|
const plan = await licenseService.getPlan(orgId);
|
||||||
if (plan?.memberLimit && plan.membersUsed >= plan.memberLimit) {
|
if (plan?.slug !== "enterprise" && plan?.memberLimit && plan.membersUsed >= plan.memberLimit) {
|
||||||
// limit imposed on number of members allowed / number of members used exceeds the number of members allowed
|
// limit imposed on number of members allowed / number of members used exceeds the number of members allowed
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Failed to create new member via SAML due to member limit reached. Upgrade plan to add more members."
|
message: "Failed to create new member via SAML due to member limit reached. Upgrade plan to add more members."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plan?.identityLimit && plan.identitiesUsed >= plan.identityLimit) {
|
if (plan?.slug !== "enterprise" && plan?.identityLimit && plan.identitiesUsed >= plan.identityLimit) {
|
||||||
// limit imposed on number of identities allowed / number of identities used exceeds the number of identities allowed
|
// limit imposed on number of identities allowed / number of identities used exceeds the number of identities allowed
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Failed to create new member via SAML due to member limit reached. Upgrade plan to add more members."
|
message: "Failed to create new member via SAML due to member limit reached. Upgrade plan to add more members."
|
||||||
|
@ -531,7 +531,7 @@ export const scimServiceFactory = ({
|
|||||||
firstName: scimUser.name.givenName,
|
firstName: scimUser.name.givenName,
|
||||||
email: scimUser.emails[0].value,
|
email: scimUser.emails[0].value,
|
||||||
lastName: scimUser.name.familyName,
|
lastName: scimUser.name.familyName,
|
||||||
isEmailVerified: hasEmailChanged ? trustScimEmails : true
|
isEmailVerified: hasEmailChanged ? trustScimEmails : undefined
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@ -790,6 +790,21 @@ export const scimServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const newGroup = await groupDAL.transaction(async (tx) => {
|
const newGroup = await groupDAL.transaction(async (tx) => {
|
||||||
|
const conflictingGroup = await groupDAL.findOne(
|
||||||
|
{
|
||||||
|
name: displayName,
|
||||||
|
orgId
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
|
||||||
|
if (conflictingGroup) {
|
||||||
|
throw new ScimRequestError({
|
||||||
|
detail: `Group with name '${displayName}' already exists in the organization`,
|
||||||
|
status: 409
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const group = await groupDAL.create(
|
const group = await groupDAL.create(
|
||||||
{
|
{
|
||||||
name: displayName,
|
name: displayName,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import picomatch from "picomatch";
|
import picomatch from "picomatch";
|
||||||
|
|
||||||
import { ProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
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";
|
||||||
@ -79,14 +79,14 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
if (!groupApprovers.length && approvals > approvers.length)
|
if (!groupApprovers.length && approvals > approvers.length)
|
||||||
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
ProjectPermissionSub.SecretApproval
|
ProjectPermissionSub.SecretApproval
|
||||||
@ -193,14 +193,14 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
secretApprovalPolicy.projectId,
|
projectId: secretApprovalPolicy.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(actorOrgId);
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
@ -288,14 +288,14 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
if (!sapPolicy)
|
if (!sapPolicy)
|
||||||
throw new NotFoundError({ message: `Secret approval policy with ID '${secretPolicyId}' not found` });
|
throw new NotFoundError({ message: `Secret approval policy with ID '${secretPolicyId}' not found` });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
sapPolicy.projectId,
|
projectId: sapPolicy.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Delete,
|
ProjectPermissionActions.Delete,
|
||||||
ProjectPermissionSub.SecretApproval
|
ProjectPermissionSub.SecretApproval
|
||||||
@ -328,13 +328,14 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
projectId
|
projectId
|
||||||
}: TListSapDTO) => {
|
}: TListSapDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
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);
|
||||||
|
|
||||||
const sapPolicies = await secretApprovalPolicyDAL.find({ projectId, deletedAt: null });
|
const sapPolicies = await secretApprovalPolicyDAL.find({ projectId, deletedAt: null });
|
||||||
@ -372,7 +373,14 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
environment,
|
environment,
|
||||||
secretPath
|
secretPath
|
||||||
}: TGetBoardSapDTO) => {
|
}: TGetBoardSapDTO) => {
|
||||||
await permissionService.getProjectPermission(actor, actorId, projectId, actorAuthMethod, actorOrgId);
|
await permissionService.getProjectPermission({
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
projectId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
|
|
||||||
return getSecretApprovalPolicy(projectId, environment, secretPath);
|
return getSecretApprovalPolicy(projectId, environment, secretPath);
|
||||||
};
|
};
|
||||||
@ -392,13 +400,14 @@ export const secretApprovalPolicyServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
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);
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ActionProjectType,
|
||||||
ProjectMembershipRole,
|
ProjectMembershipRole,
|
||||||
ProjectType,
|
|
||||||
SecretEncryptionAlgo,
|
SecretEncryptionAlgo,
|
||||||
SecretKeyEncoding,
|
SecretKeyEncoding,
|
||||||
SecretType,
|
SecretType,
|
||||||
@ -147,13 +147,14 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
|
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
|
||||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||||
|
|
||||||
await permissionService.getProjectPermission(
|
await permissionService.getProjectPermission({
|
||||||
actor as ActorType.USER,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
|
|
||||||
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, actorId);
|
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, actorId);
|
||||||
return count;
|
return count;
|
||||||
@ -173,7 +174,14 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
}: TListApprovalsDTO) => {
|
}: TListApprovalsDTO) => {
|
||||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||||
|
|
||||||
await permissionService.getProjectPermission(actor, actorId, projectId, actorAuthMethod, actorOrgId);
|
await permissionService.getProjectPermission({
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
projectId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
|
|
||||||
const { shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
const { shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
||||||
if (shouldUseSecretV2Bridge) {
|
if (shouldUseSecretV2Bridge) {
|
||||||
@ -216,13 +224,14 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
||||||
|
|
||||||
const { policy } = secretApprovalRequest;
|
const { policy } = secretApprovalRequest;
|
||||||
const { hasRole } = await permissionService.getProjectPermission(
|
const { hasRole } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
secretApprovalRequest.committerUserId !== actorId &&
|
secretApprovalRequest.committerUserId !== actorId &&
|
||||||
@ -336,13 +345,14 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { hasRole } = await permissionService.getProjectPermission(
|
const { hasRole } = await permissionService.getProjectPermission({
|
||||||
ActorType.USER,
|
actor: ActorType.USER,
|
||||||
actorId,
|
actorId,
|
||||||
secretApprovalRequest.projectId,
|
projectId: secretApprovalRequest.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
secretApprovalRequest.committerUserId !== actorId &&
|
secretApprovalRequest.committerUserId !== actorId &&
|
||||||
@ -402,13 +412,14 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { hasRole } = await permissionService.getProjectPermission(
|
const { hasRole } = await permissionService.getProjectPermission({
|
||||||
ActorType.USER,
|
actor: ActorType.USER,
|
||||||
actorId,
|
actorId,
|
||||||
secretApprovalRequest.projectId,
|
projectId: secretApprovalRequest.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
secretApprovalRequest.committerUserId !== actorId &&
|
secretApprovalRequest.committerUserId !== actorId &&
|
||||||
@ -458,13 +469,14 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { hasRole } = await permissionService.getProjectPermission(
|
const { hasRole } = await permissionService.getProjectPermission({
|
||||||
ActorType.USER,
|
actor: ActorType.USER,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!hasRole(ProjectMembershipRole.Admin) &&
|
!hasRole(ProjectMembershipRole.Admin) &&
|
||||||
@ -889,14 +901,14 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
}: TGenerateSecretApprovalRequestDTO) => {
|
}: TGenerateSecretApprovalRequestDTO) => {
|
||||||
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
subject(ProjectPermissionSub.Secrets, { environment, secretPath })
|
||||||
@ -1170,14 +1182,14 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
if (actor === ActorType.SERVICE || actor === ActorType.Machine)
|
if (actor === ActorType.SERVICE || actor === ActorType.Machine)
|
||||||
throw new BadRequestError({ message: "Cannot use service token or machine token over protected branches" });
|
throw new BadRequestError({ message: "Cannot use service token or machine token over protected branches" });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
||||||
if (!folder)
|
if (!folder)
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
@ -1255,9 +1267,10 @@ export const secretApprovalRequestServiceFactory = ({
|
|||||||
type: SecretType.Shared
|
type: SecretType.Shared
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
if (secrets.length)
|
|
||||||
|
if (secrets.length !== secretsWithNewName.length)
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `Secret does not exist: ${secretsToUpdateStoredInDB.map((el) => el.key).join(",")}`
|
message: `Secret does not exist: ${secrets.map((el) => el.key).join(",")}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,6 +180,8 @@ export const secretRotationQueueFactory = ({
|
|||||||
provider.template.client === TDbProviderClients.MsSqlServer
|
provider.template.client === TDbProviderClients.MsSqlServer
|
||||||
? ({
|
? ({
|
||||||
encrypt: appCfg.ENABLE_MSSQL_SECRET_ROTATION_ENCRYPT,
|
encrypt: appCfg.ENABLE_MSSQL_SECRET_ROTATION_ENCRYPT,
|
||||||
|
// when ca is provided use that
|
||||||
|
trustServerCertificate: !ca,
|
||||||
cryptoCredentialsDetails: ca ? { ca } : {}
|
cryptoCredentialsDetails: ca ? { ca } : {}
|
||||||
} as Record<string, unknown>)
|
} as Record<string, unknown>)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
@ -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 { ProjectType, ProjectVersion, TableName } from "@app/db/schemas";
|
import { ActionProjectType, ProjectVersion, TableName } from "@app/db/schemas";
|
||||||
import { decryptSymmetric128BitHexKeyUTF8, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
import { decryptSymmetric128BitHexKeyUTF8, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
||||||
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";
|
||||||
@ -53,14 +53,14 @@ export const secretRotationServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
projectId
|
projectId
|
||||||
}: TProjectPermission) => {
|
}: TProjectPermission) => {
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -82,14 +82,14 @@ export const secretRotationServiceFactory = ({
|
|||||||
secretPath,
|
secretPath,
|
||||||
environment
|
environment
|
||||||
}: TCreateSecretRotationDTO) => {
|
}: TCreateSecretRotationDTO) => {
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
ProjectPermissionSub.SecretRotation
|
ProjectPermissionSub.SecretRotation
|
||||||
@ -191,13 +191,14 @@ export const secretRotationServiceFactory = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getByProjectId = async ({ actorId, projectId, actor, actorOrgId, actorAuthMethod }: TListByProjectIdDTO) => {
|
const getByProjectId = async ({ actorId, projectId, actor, actorOrgId, actorAuthMethod }: TListByProjectIdDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation);
|
||||||
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(projectId);
|
||||||
if (shouldUseSecretV2Bridge) {
|
if (shouldUseSecretV2Bridge) {
|
||||||
@ -236,14 +237,14 @@ export const secretRotationServiceFactory = ({
|
|||||||
message: "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation."
|
message: "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation."
|
||||||
});
|
});
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
doc.projectId,
|
projectId: project.id,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretRotation);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretRotation);
|
||||||
await secretRotationQueue.removeFromQueue(doc.id, doc.interval);
|
await secretRotationQueue.removeFromQueue(doc.id, doc.interval);
|
||||||
await secretRotationQueue.addToQueue(doc.id, doc.interval);
|
await secretRotationQueue.addToQueue(doc.id, doc.interval);
|
||||||
@ -254,14 +255,14 @@ export const secretRotationServiceFactory = ({
|
|||||||
const doc = await secretRotationDAL.findById(rotationId);
|
const doc = await secretRotationDAL.findById(rotationId);
|
||||||
if (!doc) throw new NotFoundError({ message: `Rotation with ID '${rotationId}' not found` });
|
if (!doc) throw new NotFoundError({ message: `Rotation with ID '${rotationId}' not found` });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
doc.projectId,
|
projectId: doc.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Delete,
|
ProjectPermissionActions.Delete,
|
||||||
ProjectPermissionSub.SecretRotation
|
ProjectPermissionSub.SecretRotation
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import { Knex } from "knex";
|
import knex, { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName, TSecretScanningGitRisksInsert } from "@app/db/schemas";
|
import { TableName, TSecretScanningGitRisksInsert } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError, GatewayTimeoutError } from "@app/lib/errors";
|
||||||
import { ormify } from "@app/lib/knex";
|
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||||
|
import { OrderByDirection } from "@app/lib/types";
|
||||||
|
|
||||||
|
import { SecretScanningResolvedStatus, TGetOrgRisksDTO } from "./secret-scanning-types";
|
||||||
|
|
||||||
export type TSecretScanningDALFactory = ReturnType<typeof secretScanningDALFactory>;
|
export type TSecretScanningDALFactory = ReturnType<typeof secretScanningDALFactory>;
|
||||||
|
|
||||||
@ -19,5 +22,70 @@ export const secretScanningDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { ...gitRiskOrm, upsert };
|
const findByOrgId = async (orgId: string, filter: TGetOrgRisksDTO["filter"], tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
// Find statements
|
||||||
|
const sqlQuery = (tx || db.replicaNode())(TableName.SecretScanningGitRisk)
|
||||||
|
// eslint-disable-next-line func-names
|
||||||
|
.where(`${TableName.SecretScanningGitRisk}.orgId`, orgId);
|
||||||
|
|
||||||
|
if (filter.repositoryNames) {
|
||||||
|
void sqlQuery.whereIn(`${TableName.SecretScanningGitRisk}.repositoryFullName`, filter.repositoryNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.resolvedStatus) {
|
||||||
|
if (filter.resolvedStatus !== SecretScanningResolvedStatus.All) {
|
||||||
|
const isResolved = filter.resolvedStatus === SecretScanningResolvedStatus.Resolved;
|
||||||
|
|
||||||
|
void sqlQuery.where(`${TableName.SecretScanningGitRisk}.isResolved`, isResolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select statements
|
||||||
|
void sqlQuery
|
||||||
|
.select(selectAllTableCols(TableName.SecretScanningGitRisk))
|
||||||
|
.limit(filter.limit)
|
||||||
|
.offset(filter.offset);
|
||||||
|
|
||||||
|
if (filter.orderBy) {
|
||||||
|
const orderDirection = filter.orderDirection || OrderByDirection.ASC;
|
||||||
|
|
||||||
|
void sqlQuery.orderBy(filter.orderBy, orderDirection);
|
||||||
|
}
|
||||||
|
|
||||||
|
const countQuery = (tx || db.replicaNode())(TableName.SecretScanningGitRisk)
|
||||||
|
.where(`${TableName.SecretScanningGitRisk}.orgId`, orgId)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
const uniqueReposQuery = (tx || db.replicaNode())(TableName.SecretScanningGitRisk)
|
||||||
|
.where(`${TableName.SecretScanningGitRisk}.orgId`, orgId)
|
||||||
|
.distinct("repositoryFullName")
|
||||||
|
.select("repositoryFullName");
|
||||||
|
|
||||||
|
// we timeout long running queries to prevent DB resource issues (2 minutes)
|
||||||
|
const docs = await sqlQuery.timeout(1000 * 120);
|
||||||
|
const uniqueRepos = await uniqueReposQuery.timeout(1000 * 120);
|
||||||
|
const totalCount = await countQuery;
|
||||||
|
|
||||||
|
return {
|
||||||
|
risks: docs,
|
||||||
|
totalCount: Number(totalCount?.[0].count),
|
||||||
|
repos: uniqueRepos
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((r) => r.repositoryFullName!)
|
||||||
|
.sort((a, b) => a.localeCompare(b))
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof knex.KnexTimeoutError) {
|
||||||
|
throw new GatewayTimeoutError({
|
||||||
|
error,
|
||||||
|
message: "Failed to fetch secret leaks due to timeout. Add more search filters."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new DatabaseError({ error });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...gitRiskOrm, upsert, findByOrgId };
|
||||||
};
|
};
|
||||||
|
@ -15,6 +15,7 @@ import { TSecretScanningDALFactory } from "./secret-scanning-dal";
|
|||||||
import { TSecretScanningQueueFactory } from "./secret-scanning-queue";
|
import { TSecretScanningQueueFactory } from "./secret-scanning-queue";
|
||||||
import {
|
import {
|
||||||
SecretScanningRiskStatus,
|
SecretScanningRiskStatus,
|
||||||
|
TGetAllOrgRisksDTO,
|
||||||
TGetOrgInstallStatusDTO,
|
TGetOrgInstallStatusDTO,
|
||||||
TGetOrgRisksDTO,
|
TGetOrgRisksDTO,
|
||||||
TInstallAppSessionDTO,
|
TInstallAppSessionDTO,
|
||||||
@ -118,11 +119,21 @@ export const secretScanningServiceFactory = ({
|
|||||||
return Boolean(appInstallation);
|
return Boolean(appInstallation);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getRisksByOrg = async ({ actor, orgId, actorId, actorAuthMethod, actorOrgId }: TGetOrgRisksDTO) => {
|
const getRisksByOrg = async ({ actor, orgId, actorId, actorAuthMethod, actorOrgId, filter }: TGetOrgRisksDTO) => {
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
|
||||||
|
|
||||||
|
const results = await secretScanningDAL.findByOrgId(orgId, filter);
|
||||||
|
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAllRisksByOrg = async ({ actor, orgId, actorId, actorAuthMethod, actorOrgId }: TGetAllOrgRisksDTO) => {
|
||||||
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
|
||||||
|
|
||||||
const risks = await secretScanningDAL.find({ orgId }, { sort: [["createdAt", "desc"]] });
|
const risks = await secretScanningDAL.find({ orgId }, { sort: [["createdAt", "desc"]] });
|
||||||
return { risks };
|
return risks;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateRiskStatus = async ({
|
const updateRiskStatus = async ({
|
||||||
@ -189,6 +200,7 @@ export const secretScanningServiceFactory = ({
|
|||||||
linkInstallationToOrg,
|
linkInstallationToOrg,
|
||||||
getOrgInstallationStatus,
|
getOrgInstallationStatus,
|
||||||
getRisksByOrg,
|
getRisksByOrg,
|
||||||
|
getAllRisksByOrg,
|
||||||
updateRiskStatus,
|
updateRiskStatus,
|
||||||
handleRepoPushEvent,
|
handleRepoPushEvent,
|
||||||
handleRepoDeleteEvent
|
handleRepoDeleteEvent
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { TOrgPermission } from "@app/lib/types";
|
import { OrderByDirection, TOrgPermission } from "@app/lib/types";
|
||||||
|
|
||||||
export enum SecretScanningRiskStatus {
|
export enum SecretScanningRiskStatus {
|
||||||
FalsePositive = "RESOLVED_FALSE_POSITIVE",
|
FalsePositive = "RESOLVED_FALSE_POSITIVE",
|
||||||
@ -7,6 +7,12 @@ export enum SecretScanningRiskStatus {
|
|||||||
Unresolved = "UNRESOLVED"
|
Unresolved = "UNRESOLVED"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum SecretScanningResolvedStatus {
|
||||||
|
All = "all",
|
||||||
|
Resolved = "resolved",
|
||||||
|
Unresolved = "unresolved"
|
||||||
|
}
|
||||||
|
|
||||||
export type TInstallAppSessionDTO = TOrgPermission;
|
export type TInstallAppSessionDTO = TOrgPermission;
|
||||||
|
|
||||||
export type TLinkInstallSessionDTO = {
|
export type TLinkInstallSessionDTO = {
|
||||||
@ -16,7 +22,22 @@ export type TLinkInstallSessionDTO = {
|
|||||||
|
|
||||||
export type TGetOrgInstallStatusDTO = TOrgPermission;
|
export type TGetOrgInstallStatusDTO = TOrgPermission;
|
||||||
|
|
||||||
export type TGetOrgRisksDTO = TOrgPermission;
|
type RiskFilter = {
|
||||||
|
offset: number;
|
||||||
|
limit: number;
|
||||||
|
orderBy?: "createdAt" | "name";
|
||||||
|
orderDirection?: OrderByDirection;
|
||||||
|
repositoryNames?: string[];
|
||||||
|
resolvedStatus?: SecretScanningResolvedStatus;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TGetOrgRisksDTO = {
|
||||||
|
filter: RiskFilter;
|
||||||
|
} & TOrgPermission;
|
||||||
|
|
||||||
|
export type TGetAllOrgRisksDTO = {
|
||||||
|
filter: Omit<RiskFilter, "offset" | "limit" | "orderBy" | "orderDirection">;
|
||||||
|
} & TOrgPermission;
|
||||||
|
|
||||||
export type TUpdateRiskStatusDTO = {
|
export type TUpdateRiskStatusDTO = {
|
||||||
riskId: string;
|
riskId: string;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
|
|
||||||
import { ProjectType, TableName, TSecretTagJunctionInsert, TSecretV2TagJunctionInsert } from "@app/db/schemas";
|
import { ActionProjectType, TableName, TSecretTagJunctionInsert, TSecretV2TagJunctionInsert } from "@app/db/schemas";
|
||||||
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
||||||
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";
|
||||||
@ -83,13 +83,14 @@ export const secretSnapshotServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
path
|
path
|
||||||
}: TProjectSnapshotCountDTO) => {
|
}: TProjectSnapshotCountDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
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);
|
||||||
|
|
||||||
// We need to check if the user has access to the secrets in the folder. If we don't do this, a user could theoretically access snapshot secret values even if they don't have read access to the secrets in the folder.
|
// We need to check if the user has access to the secrets in the folder. If we don't do this, a user could theoretically access snapshot secret values even if they don't have read access to the secrets in the folder.
|
||||||
@ -119,13 +120,14 @@ export const secretSnapshotServiceFactory = ({
|
|||||||
limit = 20,
|
limit = 20,
|
||||||
offset = 0
|
offset = 0
|
||||||
}: TProjectSnapshotListDTO) => {
|
}: TProjectSnapshotListDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
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);
|
||||||
|
|
||||||
// We need to check if the user has access to the secrets in the folder. If we don't do this, a user could theoretically access snapshot secret values even if they don't have read access to the secrets in the folder.
|
// We need to check if the user has access to the secrets in the folder. If we don't do this, a user could theoretically access snapshot secret values even if they don't have read access to the secrets in the folder.
|
||||||
@ -147,13 +149,14 @@ export const secretSnapshotServiceFactory = ({
|
|||||||
const getSnapshotData = async ({ actorId, actor, actorOrgId, actorAuthMethod, id }: TGetSnapshotDataDTO) => {
|
const getSnapshotData = async ({ actorId, actor, actorOrgId, actorAuthMethod, id }: TGetSnapshotDataDTO) => {
|
||||||
const snapshot = await snapshotDAL.findById(id);
|
const snapshot = await snapshotDAL.findById(id);
|
||||||
if (!snapshot) throw new NotFoundError({ message: `Snapshot with ID '${id}' not found` });
|
if (!snapshot) throw new NotFoundError({ message: `Snapshot with ID '${id}' not found` });
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
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);
|
||||||
const shouldUseBridge = snapshot.projectVersion === 3;
|
const shouldUseBridge = snapshot.projectVersion === 3;
|
||||||
@ -322,14 +325,14 @@ export const secretSnapshotServiceFactory = ({
|
|||||||
if (!snapshot) throw new NotFoundError({ message: `Snapshot with ID '${snapshotId}' not found` });
|
if (!snapshot) throw new NotFoundError({ message: `Snapshot with ID '${snapshotId}' not found` });
|
||||||
const shouldUseBridge = snapshot.projectVersion === 3;
|
const shouldUseBridge = snapshot.projectVersion === 3;
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
snapshot.projectId,
|
projectId: snapshot.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
ProjectPermissionSub.SecretRollback
|
ProjectPermissionSub.SecretRollback
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
|
|
||||||
import { ProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
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";
|
||||||
@ -54,15 +54,15 @@ export const sshCertificateTemplateServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SSH
|
||||||
|
});
|
||||||
|
|
||||||
ForbidOnInvalidProjectType(ProjectType.SSH);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
ProjectPermissionSub.SshCertificateTemplates
|
ProjectPermissionSub.SshCertificateTemplates
|
||||||
@ -127,15 +127,15 @@ export const sshCertificateTemplateServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
certTemplate.projectId,
|
projectId: certTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SSH
|
||||||
|
});
|
||||||
|
|
||||||
ForbidOnInvalidProjectType(ProjectType.SSH);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
ProjectPermissionSub.SshCertificateTemplates
|
ProjectPermissionSub.SshCertificateTemplates
|
||||||
@ -196,15 +196,15 @@ export const sshCertificateTemplateServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
certificateTemplate.projectId,
|
projectId: certificateTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SSH
|
||||||
|
});
|
||||||
|
|
||||||
ForbidOnInvalidProjectType(ProjectType.SSH);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Delete,
|
ProjectPermissionActions.Delete,
|
||||||
ProjectPermissionSub.SshCertificateTemplates
|
ProjectPermissionSub.SshCertificateTemplates
|
||||||
@ -223,15 +223,15 @@ export const sshCertificateTemplateServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
certTemplate.projectId,
|
projectId: certTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SSH
|
||||||
|
});
|
||||||
|
|
||||||
ForbidOnInvalidProjectType(ProjectType.SSH);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
ProjectPermissionSub.SshCertificateTemplates
|
ProjectPermissionSub.SshCertificateTemplates
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
import { ProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
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";
|
||||||
@ -65,15 +65,15 @@ export const sshCertificateAuthorityServiceFactory = ({
|
|||||||
actor,
|
actor,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TCreateSshCaDTO) => {
|
}: TCreateSshCaDTO) => {
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SSH
|
||||||
|
});
|
||||||
|
|
||||||
ForbidOnInvalidProjectType(ProjectType.SSH);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
ProjectPermissionSub.SshCertificateAuthorities
|
ProjectPermissionSub.SshCertificateAuthorities
|
||||||
@ -118,15 +118,15 @@ export const sshCertificateAuthorityServiceFactory = ({
|
|||||||
const ca = await sshCertificateAuthorityDAL.findById(caId);
|
const ca = await sshCertificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` });
|
if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SSH
|
||||||
|
});
|
||||||
|
|
||||||
ForbidOnInvalidProjectType(ProjectType.SSH);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
ProjectPermissionSub.SshCertificateAuthorities
|
ProjectPermissionSub.SshCertificateAuthorities
|
||||||
@ -187,15 +187,15 @@ export const sshCertificateAuthorityServiceFactory = ({
|
|||||||
const ca = await sshCertificateAuthorityDAL.findById(caId);
|
const ca = await sshCertificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` });
|
if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SSH
|
||||||
|
});
|
||||||
|
|
||||||
ForbidOnInvalidProjectType(ProjectType.SSH);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
ProjectPermissionSub.SshCertificateAuthorities
|
ProjectPermissionSub.SshCertificateAuthorities
|
||||||
@ -226,15 +226,15 @@ export const sshCertificateAuthorityServiceFactory = ({
|
|||||||
const ca = await sshCertificateAuthorityDAL.findById(caId);
|
const ca = await sshCertificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` });
|
if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SSH
|
||||||
|
});
|
||||||
|
|
||||||
ForbidOnInvalidProjectType(ProjectType.SSH);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Delete,
|
ProjectPermissionActions.Delete,
|
||||||
ProjectPermissionSub.SshCertificateAuthorities
|
ProjectPermissionSub.SshCertificateAuthorities
|
||||||
@ -268,15 +268,15 @@ export const sshCertificateAuthorityServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
sshCertificateTemplate.projectId,
|
projectId: sshCertificateTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SSH
|
||||||
|
});
|
||||||
|
|
||||||
ForbidOnInvalidProjectType(ProjectType.SSH);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
ProjectPermissionSub.SshCertificates
|
ProjectPermissionSub.SshCertificates
|
||||||
@ -390,15 +390,15 @@ export const sshCertificateAuthorityServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
sshCertificateTemplate.projectId,
|
projectId: sshCertificateTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SSH
|
||||||
|
});
|
||||||
|
|
||||||
ForbidOnInvalidProjectType(ProjectType.SSH);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
ProjectPermissionSub.SshCertificates
|
ProjectPermissionSub.SshCertificates
|
||||||
@ -488,15 +488,15 @@ export const sshCertificateAuthorityServiceFactory = ({
|
|||||||
const ca = await sshCertificateAuthorityDAL.findById(caId);
|
const ca = await sshCertificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` });
|
if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SSH
|
||||||
|
});
|
||||||
|
|
||||||
ForbidOnInvalidProjectType(ProjectType.SSH);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
ProjectPermissionSub.SshCertificateTemplates
|
ProjectPermissionSub.SshCertificateTemplates
|
||||||
|
@ -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 { TProjectPermission } from "@app/lib/types";
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
@ -27,13 +28,14 @@ export const trustedIpServiceFactory = ({
|
|||||||
projectDAL
|
projectDAL
|
||||||
}: TTrustedIpServiceFactoryDep) => {
|
}: TTrustedIpServiceFactoryDep) => {
|
||||||
const listIpsByProjectId = async ({ projectId, actor, actorId, actorAuthMethod, actorOrgId }: TProjectPermission) => {
|
const listIpsByProjectId = async ({ projectId, actor, actorId, actorAuthMethod, actorOrgId }: TProjectPermission) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
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({
|
||||||
projectId
|
projectId
|
||||||
@ -51,13 +53,14 @@ export const trustedIpServiceFactory = ({
|
|||||||
comment,
|
comment,
|
||||||
isActive
|
isActive
|
||||||
}: TCreateIpDTO) => {
|
}: TCreateIpDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
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);
|
||||||
|
|
||||||
const project = await projectDAL.findById(projectId);
|
const project = await projectDAL.findById(projectId);
|
||||||
@ -96,13 +99,14 @@ export const trustedIpServiceFactory = ({
|
|||||||
comment,
|
comment,
|
||||||
trustedIpId
|
trustedIpId
|
||||||
}: TUpdateIpDTO) => {
|
}: TUpdateIpDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
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);
|
||||||
|
|
||||||
const project = await projectDAL.findById(projectId);
|
const project = await projectDAL.findById(projectId);
|
||||||
@ -141,13 +145,14 @@ export const trustedIpServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
trustedIpId
|
trustedIpId
|
||||||
}: TDeleteIpDTO) => {
|
}: TDeleteIpDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
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);
|
||||||
|
|
||||||
const project = await projectDAL.findById(projectId);
|
const project = await projectDAL.findById(projectId);
|
||||||
|
@ -23,6 +23,8 @@ export const KeyStorePrefixes = {
|
|||||||
`sync-integration-mutex-${projectId}-${environmentSlug}-${secretPath}` as const,
|
`sync-integration-mutex-${projectId}-${environmentSlug}-${secretPath}` as const,
|
||||||
SyncSecretIntegrationLastRunTimestamp: (projectId: string, environmentSlug: string, secretPath: string) =>
|
SyncSecretIntegrationLastRunTimestamp: (projectId: string, environmentSlug: string, secretPath: string) =>
|
||||||
`sync-integration-last-run-${projectId}-${environmentSlug}-${secretPath}` as const,
|
`sync-integration-last-run-${projectId}-${environmentSlug}-${secretPath}` as const,
|
||||||
|
SecretSyncLock: (syncId: string) => `secret-sync-mutex-${syncId}` as const,
|
||||||
|
SecretSyncLastRunTimestamp: (syncId: string) => `secret-sync-last-run-${syncId}` as const,
|
||||||
IdentityAccessTokenStatusUpdate: (identityAccessTokenId: string) =>
|
IdentityAccessTokenStatusUpdate: (identityAccessTokenId: string) =>
|
||||||
`identity-access-token-status:${identityAccessTokenId}`,
|
`identity-access-token-status:${identityAccessTokenId}`,
|
||||||
ServiceTokenStatusUpdate: (serviceTokenId: string) => `service-token-status:${serviceTokenId}`
|
ServiceTokenStatusUpdate: (serviceTokenId: string) => `service-token-status:${serviceTokenId}`
|
||||||
@ -30,6 +32,7 @@ export const KeyStorePrefixes = {
|
|||||||
|
|
||||||
export const KeyStoreTtls = {
|
export const KeyStoreTtls = {
|
||||||
SetSyncSecretIntegrationLastRunTimestampInSeconds: 60,
|
SetSyncSecretIntegrationLastRunTimestampInSeconds: 60,
|
||||||
|
SetSecretSyncLastRunTimestampInSeconds: 60,
|
||||||
AccessTokenStatusUpdateInSeconds: 120
|
AccessTokenStatusUpdateInSeconds: 120
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
import { APP_CONNECTION_NAME_MAP } from "@app/services/app-connection/app-connection-maps";
|
import { APP_CONNECTION_NAME_MAP } from "@app/services/app-connection/app-connection-maps";
|
||||||
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
import { SECRET_SYNC_CONNECTION_MAP, SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps";
|
||||||
|
|
||||||
export const GROUPS = {
|
export const GROUPS = {
|
||||||
CREATE: {
|
CREATE: {
|
||||||
@ -474,7 +476,7 @@ export const PROJECTS = {
|
|||||||
},
|
},
|
||||||
ADD_GROUP_TO_PROJECT: {
|
ADD_GROUP_TO_PROJECT: {
|
||||||
projectId: "The ID of the project to add the group to.",
|
projectId: "The ID of the project to add the group to.",
|
||||||
groupId: "The ID of the group to add to the project.",
|
groupIdOrName: "The ID or name of the group to add to the project.",
|
||||||
role: "The role for the group to assume in the project."
|
role: "The role for the group to assume in the project."
|
||||||
},
|
},
|
||||||
UPDATE_GROUP_IN_PROJECT: {
|
UPDATE_GROUP_IN_PROJECT: {
|
||||||
@ -1643,6 +1645,83 @@ export const AppConnections = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
DELETE: (app: AppConnection) => ({
|
DELETE: (app: AppConnection) => ({
|
||||||
connectionId: `The ID of the ${APP_CONNECTION_NAME_MAP[app]} connection to be deleted.`
|
connectionId: `The ID of the ${APP_CONNECTION_NAME_MAP[app]} Connection to be deleted.`
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SecretSyncs = {
|
||||||
|
LIST: (destination?: SecretSync) => ({
|
||||||
|
projectId: `The ID of the project to list ${destination ? SECRET_SYNC_NAME_MAP[destination] : "Secret"} Syncs from.`
|
||||||
|
}),
|
||||||
|
GET_BY_ID: (destination: SecretSync) => ({
|
||||||
|
syncId: `The ID of the ${SECRET_SYNC_NAME_MAP[destination]} Sync to retrieve.`
|
||||||
|
}),
|
||||||
|
GET_BY_NAME: (destination: SecretSync) => ({
|
||||||
|
syncName: `The name of the ${SECRET_SYNC_NAME_MAP[destination]} Sync to retrieve.`,
|
||||||
|
projectId: `The ID of the project the ${SECRET_SYNC_NAME_MAP[destination]} Sync is associated with.`
|
||||||
|
}),
|
||||||
|
CREATE: (destination: SecretSync) => {
|
||||||
|
const destinationName = SECRET_SYNC_NAME_MAP[destination];
|
||||||
|
return {
|
||||||
|
name: `The name of the ${destinationName} Sync to create. Must be slug-friendly.`,
|
||||||
|
description: `An optional description for the ${destinationName} Sync.`,
|
||||||
|
projectId: "The ID of the project to create the sync in.",
|
||||||
|
environment: `The slug of the project environment to sync secrets from.`,
|
||||||
|
secretPath: `The folder path to sync secrets from.`,
|
||||||
|
connectionId: `The ID of the ${
|
||||||
|
APP_CONNECTION_NAME_MAP[SECRET_SYNC_CONNECTION_MAP[destination]]
|
||||||
|
} Connection to use for syncing.`,
|
||||||
|
isAutoSyncEnabled: `Whether secrets should be automatically synced when changes occur at the source location or not.`,
|
||||||
|
syncOptions: "Optional parameters to modify how secrets are synced."
|
||||||
|
};
|
||||||
|
},
|
||||||
|
UPDATE: (destination: SecretSync) => {
|
||||||
|
const destinationName = SECRET_SYNC_NAME_MAP[destination];
|
||||||
|
return {
|
||||||
|
syncId: `The ID of the ${destinationName} Sync to be updated.`,
|
||||||
|
connectionId: `The updated ID of the ${
|
||||||
|
APP_CONNECTION_NAME_MAP[SECRET_SYNC_CONNECTION_MAP[destination]]
|
||||||
|
} Connection to use for syncing.`,
|
||||||
|
name: `The updated name of the ${destinationName} Sync. Must be slug-friendly.`,
|
||||||
|
environment: `The updated slug of the project environment to sync secrets from.`,
|
||||||
|
secretPath: `The updated folder path to sync secrets from.`,
|
||||||
|
description: `The updated description of the ${destinationName} Sync.`,
|
||||||
|
isAutoSyncEnabled: `Whether secrets should be automatically synced when changes occur at the source location or not.`,
|
||||||
|
syncOptions: "Optional parameters to modify how secrets are synced."
|
||||||
|
};
|
||||||
|
},
|
||||||
|
DELETE: (destination: SecretSync) => ({
|
||||||
|
syncId: `The ID of the ${SECRET_SYNC_NAME_MAP[destination]} Sync to be deleted.`,
|
||||||
|
removeSecrets: `Whether previously synced secrets should be removed prior to deletion.`
|
||||||
|
}),
|
||||||
|
SYNC_SECRETS: (destination: SecretSync) => ({
|
||||||
|
syncId: `The ID of the ${SECRET_SYNC_NAME_MAP[destination]} Sync to trigger a sync for.`
|
||||||
|
}),
|
||||||
|
IMPORT_SECRETS: (destination: SecretSync) => ({
|
||||||
|
syncId: `The ID of the ${SECRET_SYNC_NAME_MAP[destination]} Sync to trigger importing secrets for.`,
|
||||||
|
importBehavior: `Specify whether Infisical should prioritize secret values from Infisical or ${SECRET_SYNC_NAME_MAP[destination]}.`
|
||||||
|
}),
|
||||||
|
REMOVE_SECRETS: (destination: SecretSync) => ({
|
||||||
|
syncId: `The ID of the ${SECRET_SYNC_NAME_MAP[destination]} Sync to trigger removing secrets for.`
|
||||||
|
}),
|
||||||
|
SYNC_OPTIONS: (destination: SecretSync) => {
|
||||||
|
const destinationName = SECRET_SYNC_NAME_MAP[destination];
|
||||||
|
return {
|
||||||
|
INITIAL_SYNC_BEHAVIOR: `Specify how Infisical should resolve the initial sync to the ${destinationName} destination.`,
|
||||||
|
PREPEND_PREFIX: `Optionally prepend a prefix to your secrets' keys when syncing to ${destinationName}.`,
|
||||||
|
APPEND_SUFFIX: `Optionally append a suffix to your secrets' keys when syncing to ${destinationName}.`
|
||||||
|
};
|
||||||
|
},
|
||||||
|
DESTINATION_CONFIG: {
|
||||||
|
AWS_PARAMETER_STORE: {
|
||||||
|
REGION: "The AWS region to sync secrets to.",
|
||||||
|
PATH: "The Parameter Store path to sync secrets to."
|
||||||
|
},
|
||||||
|
GITHUB: {
|
||||||
|
ORG: "The name of the GitHub organization.",
|
||||||
|
OWNER: "The name of the GitHub account owner of the repository.",
|
||||||
|
REPO: "The name of the GitHub repository.",
|
||||||
|
ENV: "The name of the GitHub environment."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -199,7 +199,29 @@ const envSchema = z
|
|||||||
INF_APP_CONNECTION_GITHUB_APP_CLIENT_SECRET: zpStr(z.string().optional()),
|
INF_APP_CONNECTION_GITHUB_APP_CLIENT_SECRET: zpStr(z.string().optional()),
|
||||||
INF_APP_CONNECTION_GITHUB_APP_PRIVATE_KEY: zpStr(z.string().optional()),
|
INF_APP_CONNECTION_GITHUB_APP_PRIVATE_KEY: zpStr(z.string().optional()),
|
||||||
INF_APP_CONNECTION_GITHUB_APP_SLUG: zpStr(z.string().optional()),
|
INF_APP_CONNECTION_GITHUB_APP_SLUG: zpStr(z.string().optional()),
|
||||||
INF_APP_CONNECTION_GITHUB_APP_ID: zpStr(z.string().optional())
|
INF_APP_CONNECTION_GITHUB_APP_ID: zpStr(z.string().optional()),
|
||||||
|
|
||||||
|
/* CORS ----------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
CORS_ALLOWED_ORIGINS: zpStr(
|
||||||
|
z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform((val) => {
|
||||||
|
if (!val) return undefined;
|
||||||
|
return JSON.parse(val) as string[];
|
||||||
|
})
|
||||||
|
),
|
||||||
|
|
||||||
|
CORS_ALLOWED_HEADERS: zpStr(
|
||||||
|
z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform((val) => {
|
||||||
|
if (!val) return undefined;
|
||||||
|
return JSON.parse(val) as string[];
|
||||||
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
// To ensure that basic encryption is always possible.
|
// To ensure that basic encryption is always possible.
|
||||||
.refine(
|
.refine(
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
export { isDisposableEmail } from "./validate-email";
|
export { isDisposableEmail } from "./validate-email";
|
||||||
export { isValidFolderName, isValidSecretPath } from "./validate-folder-name";
|
export { isValidFolderName, isValidSecretPath } from "./validate-folder-name";
|
||||||
export { blockLocalAndPrivateIpAddresses } from "./validate-url";
|
export { blockLocalAndPrivateIpAddresses } from "./validate-url";
|
||||||
|
export { isUuidV4 } from "./validate-uuid";
|
||||||
|
3
backend/src/lib/validator/validate-uuid.ts
Normal file
3
backend/src/lib/validator/validate-uuid.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const isUuidV4 = (uuid: string) => z.string().uuid().safeParse(uuid).success;
|
@ -15,6 +15,12 @@ import {
|
|||||||
TIntegrationSyncPayload,
|
TIntegrationSyncPayload,
|
||||||
TSyncSecretsDTO
|
TSyncSecretsDTO
|
||||||
} from "@app/services/secret/secret-types";
|
} from "@app/services/secret/secret-types";
|
||||||
|
import {
|
||||||
|
TQueueSecretSyncImportSecretsByIdDTO,
|
||||||
|
TQueueSecretSyncRemoveSecretsByIdDTO,
|
||||||
|
TQueueSecretSyncSyncSecretsByIdDTO,
|
||||||
|
TQueueSendSecretSyncActionFailedNotificationsDTO
|
||||||
|
} from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
export enum QueueName {
|
export enum QueueName {
|
||||||
SecretRotation = "secret-rotation",
|
SecretRotation = "secret-rotation",
|
||||||
@ -36,7 +42,8 @@ export enum QueueName {
|
|||||||
SecretSync = "secret-sync", // parent queue to push integration sync, webhook, and secret replication
|
SecretSync = "secret-sync", // parent queue to push integration sync, webhook, and secret replication
|
||||||
ProjectV3Migration = "project-v3-migration",
|
ProjectV3Migration = "project-v3-migration",
|
||||||
AccessTokenStatusUpdate = "access-token-status-update",
|
AccessTokenStatusUpdate = "access-token-status-update",
|
||||||
ImportSecretsFromExternalSource = "import-secrets-from-external-source"
|
ImportSecretsFromExternalSource = "import-secrets-from-external-source",
|
||||||
|
AppConnectionSecretSync = "app-connection-secret-sync"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum QueueJobs {
|
export enum QueueJobs {
|
||||||
@ -61,7 +68,11 @@ export enum QueueJobs {
|
|||||||
ProjectV3Migration = "project-v3-migration",
|
ProjectV3Migration = "project-v3-migration",
|
||||||
IdentityAccessTokenStatusUpdate = "identity-access-token-status-update",
|
IdentityAccessTokenStatusUpdate = "identity-access-token-status-update",
|
||||||
ServiceTokenStatusUpdate = "service-token-status-update",
|
ServiceTokenStatusUpdate = "service-token-status-update",
|
||||||
ImportSecretsFromExternalSource = "import-secrets-from-external-source"
|
ImportSecretsFromExternalSource = "import-secrets-from-external-source",
|
||||||
|
SecretSyncSyncSecrets = "secret-sync-sync-secrets",
|
||||||
|
SecretSyncImportSecrets = "secret-sync-import-secrets",
|
||||||
|
SecretSyncRemoveSecrets = "secret-sync-remove-secrets",
|
||||||
|
SecretSyncSendActionFailedNotifications = "secret-sync-send-action-failed-notifications"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TQueueJobTypes = {
|
export type TQueueJobTypes = {
|
||||||
@ -184,6 +195,23 @@ export type TQueueJobTypes = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
[QueueName.AppConnectionSecretSync]:
|
||||||
|
| {
|
||||||
|
name: QueueJobs.SecretSyncSyncSecrets;
|
||||||
|
payload: TQueueSecretSyncSyncSecretsByIdDTO;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: QueueJobs.SecretSyncImportSecrets;
|
||||||
|
payload: TQueueSecretSyncImportSecretsByIdDTO;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: QueueJobs.SecretSyncRemoveSecrets;
|
||||||
|
payload: TQueueSecretSyncRemoveSecretsByIdDTO;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: QueueJobs.SecretSyncSendActionFailedNotifications;
|
||||||
|
payload: TQueueSendSecretSyncActionFailedNotificationsDTO;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TQueueServiceFactory = ReturnType<typeof queueServiceFactory>;
|
export type TQueueServiceFactory = ReturnType<typeof queueServiceFactory>;
|
||||||
|
@ -87,7 +87,16 @@ export const main = async ({ db, hsmModule, auditLogDb, smtp, logger, queue, key
|
|||||||
|
|
||||||
await server.register<FastifyCorsOptions>(cors, {
|
await server.register<FastifyCorsOptions>(cors, {
|
||||||
credentials: true,
|
credentials: true,
|
||||||
origin: appCfg.SITE_URL || true
|
...(appCfg.CORS_ALLOWED_ORIGINS?.length
|
||||||
|
? {
|
||||||
|
origin: [...appCfg.CORS_ALLOWED_ORIGINS, ...(appCfg.SITE_URL ? [appCfg.SITE_URL] : [])]
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
origin: appCfg.SITE_URL || true
|
||||||
|
}),
|
||||||
|
...(appCfg.CORS_ALLOWED_HEADERS?.length && {
|
||||||
|
allowedHeaders: appCfg.CORS_ALLOWED_HEADERS
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.register(addErrorsToResponseSchemas);
|
await server.register(addErrorsToResponseSchemas);
|
||||||
|
@ -32,13 +32,21 @@ export const getUserAgentType = (userAgent: string | undefined) => {
|
|||||||
export const injectAuditLogInfo = fp(async (server: FastifyZodProvider) => {
|
export const injectAuditLogInfo = fp(async (server: FastifyZodProvider) => {
|
||||||
server.decorateRequest("auditLogInfo", null);
|
server.decorateRequest("auditLogInfo", null);
|
||||||
server.addHook("onRequest", async (req) => {
|
server.addHook("onRequest", async (req) => {
|
||||||
if (!req.auth) return;
|
|
||||||
const userAgent = req.headers["user-agent"] ?? "";
|
const userAgent = req.headers["user-agent"] ?? "";
|
||||||
const payload = {
|
const payload = {
|
||||||
ipAddress: req.realIp,
|
ipAddress: req.realIp,
|
||||||
userAgent,
|
userAgent,
|
||||||
userAgentType: getUserAgentType(userAgent)
|
userAgentType: getUserAgentType(userAgent)
|
||||||
} as typeof req.auditLogInfo;
|
} as typeof req.auditLogInfo;
|
||||||
|
|
||||||
|
if (!req.auth) {
|
||||||
|
payload.actor = {
|
||||||
|
type: ActorType.UNKNOWN_USER,
|
||||||
|
metadata: {}
|
||||||
|
};
|
||||||
|
req.auditLogInfo = payload;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (req.auth.actor === ActorType.USER) {
|
if (req.auth.actor === ActorType.USER) {
|
||||||
payload.actor = {
|
payload.actor = {
|
||||||
type: ActorType.USER,
|
type: ActorType.USER,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import type { EmitterWebhookEventName } from "@octokit/webhooks/dist-types/types";
|
||||||
import { PushEvent } from "@octokit/webhooks-types";
|
import { PushEvent } from "@octokit/webhooks-types";
|
||||||
import { Probot } from "probot";
|
import { Probot } from "probot";
|
||||||
import SmeeClient from "smee-client";
|
import SmeeClient from "smee-client";
|
||||||
@ -54,14 +55,14 @@ export const registerSecretScannerGhApp = async (server: FastifyZodProvider) =>
|
|||||||
rateLimit: writeLimit
|
rateLimit: writeLimit
|
||||||
},
|
},
|
||||||
handler: async (req, res) => {
|
handler: async (req, res) => {
|
||||||
const eventName = req.headers["x-github-event"];
|
const eventName = req.headers["x-github-event"] as EmitterWebhookEventName;
|
||||||
const signatureSHA256 = req.headers["x-hub-signature-256"] as string;
|
const signatureSHA256 = req.headers["x-hub-signature-256"] as string;
|
||||||
const id = req.headers["x-github-delivery"] as string;
|
const id = req.headers["x-github-delivery"] as string;
|
||||||
|
|
||||||
await probot.webhooks.verifyAndReceive({
|
await probot.webhooks.verifyAndReceive({
|
||||||
id,
|
id,
|
||||||
// @ts-expect-error type
|
|
||||||
name: eventName,
|
name: eventName,
|
||||||
payload: req.body as string,
|
payload: JSON.stringify(req.body),
|
||||||
signature: signatureSHA256
|
signature: signatureSHA256
|
||||||
});
|
});
|
||||||
void res.send("ok");
|
void res.send("ok");
|
||||||
|
@ -196,6 +196,9 @@ import { secretImportDALFactory } from "@app/services/secret-import/secret-impor
|
|||||||
import { secretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
|
import { secretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
|
||||||
import { secretSharingDALFactory } from "@app/services/secret-sharing/secret-sharing-dal";
|
import { secretSharingDALFactory } from "@app/services/secret-sharing/secret-sharing-dal";
|
||||||
import { secretSharingServiceFactory } from "@app/services/secret-sharing/secret-sharing-service";
|
import { secretSharingServiceFactory } from "@app/services/secret-sharing/secret-sharing-service";
|
||||||
|
import { secretSyncDALFactory } from "@app/services/secret-sync/secret-sync-dal";
|
||||||
|
import { secretSyncQueueFactory } from "@app/services/secret-sync/secret-sync-queue";
|
||||||
|
import { secretSyncServiceFactory } from "@app/services/secret-sync/secret-sync-service";
|
||||||
import { secretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
|
import { secretTagDALFactory } from "@app/services/secret-tag/secret-tag-dal";
|
||||||
import { secretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service";
|
import { secretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service";
|
||||||
import { secretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal";
|
import { secretV2BridgeDALFactory } from "@app/services/secret-v2-bridge/secret-v2-bridge-dal";
|
||||||
@ -318,6 +321,7 @@ export const registerRoutes = async (
|
|||||||
const trustedIpDAL = trustedIpDALFactory(db);
|
const trustedIpDAL = trustedIpDALFactory(db);
|
||||||
const telemetryDAL = telemetryDALFactory(db);
|
const telemetryDAL = telemetryDALFactory(db);
|
||||||
const appConnectionDAL = appConnectionDALFactory(db);
|
const appConnectionDAL = appConnectionDALFactory(db);
|
||||||
|
const secretSyncDAL = secretSyncDALFactory(db, folderDAL);
|
||||||
|
|
||||||
// ee db layer ops
|
// ee db layer ops
|
||||||
const permissionDAL = permissionDALFactory(db);
|
const permissionDAL = permissionDALFactory(db);
|
||||||
@ -608,6 +612,7 @@ export const registerRoutes = async (
|
|||||||
});
|
});
|
||||||
const superAdminService = superAdminServiceFactory({
|
const superAdminService = superAdminServiceFactory({
|
||||||
userDAL,
|
userDAL,
|
||||||
|
userAliasDAL,
|
||||||
authService: loginService,
|
authService: loginService,
|
||||||
serverCfgDAL: superAdminDAL,
|
serverCfgDAL: superAdminDAL,
|
||||||
kmsRootConfigDAL,
|
kmsRootConfigDAL,
|
||||||
@ -823,6 +828,29 @@ export const registerRoutes = async (
|
|||||||
kmsService
|
kmsService
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const secretSyncQueue = secretSyncQueueFactory({
|
||||||
|
queueService,
|
||||||
|
secretSyncDAL,
|
||||||
|
folderDAL,
|
||||||
|
secretImportDAL,
|
||||||
|
secretV2BridgeDAL,
|
||||||
|
kmsService,
|
||||||
|
keyStore,
|
||||||
|
auditLogService,
|
||||||
|
smtpService,
|
||||||
|
projectDAL,
|
||||||
|
projectMembershipDAL,
|
||||||
|
projectBotDAL,
|
||||||
|
secretDAL,
|
||||||
|
secretBlindIndexDAL,
|
||||||
|
secretVersionDAL,
|
||||||
|
secretTagDAL,
|
||||||
|
secretVersionTagDAL,
|
||||||
|
secretVersionV2BridgeDAL,
|
||||||
|
secretVersionTagV2BridgeDAL,
|
||||||
|
resourceMetadataDAL
|
||||||
|
});
|
||||||
|
|
||||||
const secretQueueService = secretQueueFactory({
|
const secretQueueService = secretQueueFactory({
|
||||||
keyStore,
|
keyStore,
|
||||||
queueService,
|
queueService,
|
||||||
@ -857,7 +885,8 @@ export const registerRoutes = async (
|
|||||||
projectKeyDAL,
|
projectKeyDAL,
|
||||||
projectUserMembershipRoleDAL,
|
projectUserMembershipRoleDAL,
|
||||||
orgService,
|
orgService,
|
||||||
resourceMetadataDAL
|
resourceMetadataDAL,
|
||||||
|
secretSyncQueue
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectService = projectServiceFactory({
|
const projectService = projectServiceFactory({
|
||||||
@ -894,7 +923,8 @@ export const registerRoutes = async (
|
|||||||
certificateTemplateDAL,
|
certificateTemplateDAL,
|
||||||
projectSlackConfigDAL,
|
projectSlackConfigDAL,
|
||||||
slackIntegrationDAL,
|
slackIntegrationDAL,
|
||||||
projectTemplateService
|
projectTemplateService,
|
||||||
|
groupProjectDAL
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectEnvService = projectEnvServiceFactory({
|
const projectEnvService = projectEnvServiceFactory({
|
||||||
@ -1367,8 +1397,17 @@ export const registerRoutes = async (
|
|||||||
const appConnectionService = appConnectionServiceFactory({
|
const appConnectionService = appConnectionServiceFactory({
|
||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
kmsService,
|
kmsService
|
||||||
licenseService
|
});
|
||||||
|
|
||||||
|
const secretSyncService = secretSyncServiceFactory({
|
||||||
|
secretSyncDAL,
|
||||||
|
permissionService,
|
||||||
|
appConnectionService,
|
||||||
|
folderDAL,
|
||||||
|
secretSyncQueue,
|
||||||
|
projectBotService,
|
||||||
|
keyStore
|
||||||
});
|
});
|
||||||
|
|
||||||
await superAdminService.initServerCfg();
|
await superAdminService.initServerCfg();
|
||||||
@ -1468,7 +1507,8 @@ export const registerRoutes = async (
|
|||||||
externalGroupOrgRoleMapping: externalGroupOrgRoleMappingService,
|
externalGroupOrgRoleMapping: externalGroupOrgRoleMappingService,
|
||||||
projectTemplate: projectTemplateService,
|
projectTemplate: projectTemplateService,
|
||||||
totp: totpService,
|
totp: totpService,
|
||||||
appConnection: appConnectionService
|
appConnection: appConnectionService,
|
||||||
|
secretSync: secretSyncService
|
||||||
});
|
});
|
||||||
|
|
||||||
const cronJobs: CronJob[] = [];
|
const cronJobs: CronJob[] = [];
|
||||||
|
@ -15,7 +15,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
|||||||
app,
|
app,
|
||||||
createSchema,
|
createSchema,
|
||||||
updateSchema,
|
updateSchema,
|
||||||
responseSchema
|
sanitizedResponseSchema
|
||||||
}: {
|
}: {
|
||||||
app: AppConnection;
|
app: AppConnection;
|
||||||
server: FastifyZodProvider;
|
server: FastifyZodProvider;
|
||||||
@ -26,7 +26,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
|||||||
description?: string | null;
|
description?: string | null;
|
||||||
}>;
|
}>;
|
||||||
updateSchema: z.ZodType<{ name?: string; credentials?: I["credentials"]; description?: string | null }>;
|
updateSchema: z.ZodType<{ name?: string; credentials?: I["credentials"]; description?: string | null }>;
|
||||||
responseSchema: z.ZodTypeAny;
|
sanitizedResponseSchema: z.ZodTypeAny;
|
||||||
}) => {
|
}) => {
|
||||||
const appName = APP_CONNECTION_NAME_MAP[app];
|
const appName = APP_CONNECTION_NAME_MAP[app];
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
|||||||
schema: {
|
schema: {
|
||||||
description: `List the ${appName} Connections for the current organization.`,
|
description: `List the ${appName} Connections for the current organization.`,
|
||||||
response: {
|
response: {
|
||||||
200: z.object({ appConnections: responseSchema.array() })
|
200: z.object({ appConnections: sanitizedResponseSchema.array() })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
@ -63,6 +63,44 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/available",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `List the ${appName} Connections the current user has permission to establish connections with.`,
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
appConnections: z.object({ app: z.literal(app), name: z.string(), id: z.string().uuid() }).array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const appConnections = await server.services.appConnection.listAvailableAppConnectionsForUser(
|
||||||
|
app,
|
||||||
|
req.permission
|
||||||
|
);
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.GET_AVAILABLE_APP_CONNECTIONS_DETAILS,
|
||||||
|
metadata: {
|
||||||
|
app,
|
||||||
|
count: appConnections.length,
|
||||||
|
connectionIds: appConnections.map((connection) => connection.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { appConnections };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:connectionId",
|
url: "/:connectionId",
|
||||||
@ -75,7 +113,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
|||||||
connectionId: z.string().uuid().describe(AppConnections.GET_BY_ID(app).connectionId)
|
connectionId: z.string().uuid().describe(AppConnections.GET_BY_ID(app).connectionId)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({ appConnection: responseSchema })
|
200: z.object({ appConnection: sanitizedResponseSchema })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
@ -105,7 +143,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
|||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/name/:connectionName`,
|
url: `/connection-name/:connectionName`,
|
||||||
config: {
|
config: {
|
||||||
rateLimit: readLimit
|
rateLimit: readLimit
|
||||||
},
|
},
|
||||||
@ -114,11 +152,12 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
|||||||
params: z.object({
|
params: z.object({
|
||||||
connectionName: z
|
connectionName: z
|
||||||
.string()
|
.string()
|
||||||
.min(0, "Connection name required")
|
.trim()
|
||||||
|
.min(1, "Connection name required")
|
||||||
.describe(AppConnections.GET_BY_NAME(app).connectionName)
|
.describe(AppConnections.GET_BY_NAME(app).connectionName)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({ appConnection: responseSchema })
|
200: z.object({ appConnection: sanitizedResponseSchema })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
@ -158,7 +197,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
|||||||
} ${appName} Connection for the current organization.`,
|
} ${appName} Connection for the current organization.`,
|
||||||
body: createSchema,
|
body: createSchema,
|
||||||
response: {
|
response: {
|
||||||
200: z.object({ appConnection: responseSchema })
|
200: z.object({ appConnection: sanitizedResponseSchema })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
@ -168,7 +207,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
|||||||
const appConnection = (await server.services.appConnection.createAppConnection(
|
const appConnection = (await server.services.appConnection.createAppConnection(
|
||||||
{ name, method, app, credentials, description },
|
{ name, method, app, credentials, description },
|
||||||
req.permission
|
req.permission
|
||||||
)) as TAppConnection;
|
)) as T;
|
||||||
|
|
||||||
await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
@ -201,7 +240,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
|||||||
}),
|
}),
|
||||||
body: updateSchema,
|
body: updateSchema,
|
||||||
response: {
|
response: {
|
||||||
200: z.object({ appConnection: responseSchema })
|
200: z.object({ appConnection: sanitizedResponseSchema })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
@ -244,7 +283,7 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
|||||||
connectionId: z.string().uuid().describe(AppConnections.DELETE(app).connectionId)
|
connectionId: z.string().uuid().describe(AppConnections.DELETE(app).connectionId)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({ appConnection: responseSchema })
|
200: z.object({ appConnection: sanitizedResponseSchema })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
@ -1,17 +0,0 @@
|
|||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
|
||||||
import {
|
|
||||||
CreateGitHubConnectionSchema,
|
|
||||||
SanitizedGitHubConnectionSchema,
|
|
||||||
UpdateGitHubConnectionSchema
|
|
||||||
} from "@app/services/app-connection/github";
|
|
||||||
|
|
||||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
|
||||||
|
|
||||||
export const registerGitHubConnectionRouter = async (server: FastifyZodProvider) =>
|
|
||||||
registerAppConnectionEndpoints({
|
|
||||||
app: AppConnection.GitHub,
|
|
||||||
server,
|
|
||||||
responseSchema: SanitizedGitHubConnectionSchema,
|
|
||||||
createSchema: CreateGitHubConnectionSchema,
|
|
||||||
updateSchema: UpdateGitHubConnectionSchema
|
|
||||||
});
|
|
@ -1,8 +0,0 @@
|
|||||||
import { registerAwsConnectionRouter } from "@app/server/routes/v1/app-connection-routers/apps/aws-connection-router";
|
|
||||||
import { registerGitHubConnectionRouter } from "@app/server/routes/v1/app-connection-routers/apps/github-connection-router";
|
|
||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
|
||||||
|
|
||||||
export const APP_CONNECTION_REGISTER_MAP: Record<AppConnection, (server: FastifyZodProvider) => Promise<void>> = {
|
|
||||||
[AppConnection.AWS]: registerAwsConnectionRouter,
|
|
||||||
[AppConnection.GitHub]: registerGitHubConnectionRouter
|
|
||||||
};
|
|
@ -11,7 +11,7 @@ export const registerAwsConnectionRouter = async (server: FastifyZodProvider) =>
|
|||||||
registerAppConnectionEndpoints({
|
registerAppConnectionEndpoints({
|
||||||
app: AppConnection.AWS,
|
app: AppConnection.AWS,
|
||||||
server,
|
server,
|
||||||
responseSchema: SanitizedAwsConnectionSchema,
|
sanitizedResponseSchema: SanitizedAwsConnectionSchema,
|
||||||
createSchema: CreateAwsConnectionSchema,
|
createSchema: CreateAwsConnectionSchema,
|
||||||
updateSchema: UpdateAwsConnectionSchema
|
updateSchema: UpdateAwsConnectionSchema
|
||||||
});
|
});
|
@ -0,0 +1,117 @@
|
|||||||
|
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 {
|
||||||
|
CreateGitHubConnectionSchema,
|
||||||
|
SanitizedGitHubConnectionSchema,
|
||||||
|
UpdateGitHubConnectionSchema
|
||||||
|
} from "@app/services/app-connection/github";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||||
|
|
||||||
|
export const registerGitHubConnectionRouter = async (server: FastifyZodProvider) => {
|
||||||
|
registerAppConnectionEndpoints({
|
||||||
|
app: AppConnection.GitHub,
|
||||||
|
server,
|
||||||
|
sanitizedResponseSchema: SanitizedGitHubConnectionSchema,
|
||||||
|
createSchema: CreateGitHubConnectionSchema,
|
||||||
|
updateSchema: UpdateGitHubConnectionSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
// The below endpoints are not exposed and for Infisical App use
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: `/:connectionId/repositories`,
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
connectionId: z.string().uuid()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
repositories: z
|
||||||
|
.object({ id: z.number(), name: z.string(), owner: z.object({ login: z.string(), id: z.number() }) })
|
||||||
|
.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { connectionId } = req.params;
|
||||||
|
|
||||||
|
const repositories = await server.services.appConnection.github.listRepositories(connectionId, req.permission);
|
||||||
|
|
||||||
|
return { repositories };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: `/:connectionId/organizations`,
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
connectionId: z.string().uuid()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
organizations: z.object({ id: z.number(), login: z.string() }).array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { connectionId } = req.params;
|
||||||
|
|
||||||
|
const organizations = await server.services.appConnection.github.listOrganizations(connectionId, req.permission);
|
||||||
|
|
||||||
|
return { organizations };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: `/:connectionId/environments`,
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
connectionId: z.string().uuid()
|
||||||
|
}),
|
||||||
|
querystring: z.object({
|
||||||
|
repo: z.string().min(1, "Repository name is required"),
|
||||||
|
owner: z.string().min(1, "Repository owner name is required")
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
environments: z.object({ id: z.number(), name: z.string() }).array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { connectionId } = req.params;
|
||||||
|
const { repo, owner } = req.query;
|
||||||
|
|
||||||
|
const environments = await server.services.appConnection.github.listEnvironments(
|
||||||
|
{
|
||||||
|
connectionId,
|
||||||
|
repo,
|
||||||
|
owner
|
||||||
|
},
|
||||||
|
req.permission
|
||||||
|
);
|
||||||
|
|
||||||
|
return { environments };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@ -1,2 +1,12 @@
|
|||||||
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
|
|
||||||
|
import { registerAwsConnectionRouter } from "./aws-connection-router";
|
||||||
|
import { registerGitHubConnectionRouter } from "./github-connection-router";
|
||||||
|
|
||||||
export * from "./app-connection-router";
|
export * from "./app-connection-router";
|
||||||
export * from "./apps";
|
|
||||||
|
export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server: FastifyZodProvider) => Promise<void>> =
|
||||||
|
{
|
||||||
|
[AppConnection.AWS]: registerAwsConnectionRouter,
|
||||||
|
[AppConnection.GitHub]: registerGitHubConnectionRouter
|
||||||
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { SecretFoldersSchema, SecretImportsSchema, SecretTagsSchema } from "@app/db/schemas";
|
import { ActionProjectType, SecretFoldersSchema, SecretImportsSchema, SecretTagsSchema } from "@app/db/schemas";
|
||||||
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionDynamicSecretActions,
|
ProjectPermissionDynamicSecretActions,
|
||||||
@ -220,13 +220,14 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
totalCount: totalFolderCount ?? 0
|
totalCount: totalFolderCount ?? 0
|
||||||
};
|
};
|
||||||
|
|
||||||
const { permission } = await server.services.permission.getProjectPermission(
|
const { permission } = await server.services.permission.getProjectPermission({
|
||||||
req.permission.type,
|
actor: req.permission.type,
|
||||||
req.permission.id,
|
actorId: req.permission.id,
|
||||||
projectId,
|
projectId,
|
||||||
req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
req.permission.orgId
|
actorOrgId: req.permission.orgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
|
|
||||||
const allowedDynamicSecretEnvironments = // filter envs user has access to
|
const allowedDynamicSecretEnvironments = // filter envs user has access to
|
||||||
environments.filter((environment) =>
|
environments.filter((environment) =>
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import { APP_CONNECTION_REGISTER_MAP, registerAppConnectionRouter } from "@app/server/routes/v1/app-connection-routers";
|
import {
|
||||||
|
APP_CONNECTION_REGISTER_ROUTER_MAP,
|
||||||
|
registerAppConnectionRouter
|
||||||
|
} from "@app/server/routes/v1/app-connection-routers";
|
||||||
import { registerCmekRouter } from "@app/server/routes/v1/cmek-router";
|
import { registerCmekRouter } from "@app/server/routes/v1/cmek-router";
|
||||||
import { registerDashboardRouter } from "@app/server/routes/v1/dashboard-router";
|
import { registerDashboardRouter } from "@app/server/routes/v1/dashboard-router";
|
||||||
|
import { registerSecretSyncRouter, SECRET_SYNC_REGISTER_ROUTER_MAP } from "@app/server/routes/v1/secret-sync-routers";
|
||||||
|
|
||||||
import { registerAdminRouter } from "./admin-router";
|
import { registerAdminRouter } from "./admin-router";
|
||||||
import { registerAuthRoutes } from "./auth-router";
|
import { registerAuthRoutes } from "./auth-router";
|
||||||
@ -113,12 +117,28 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
|
|||||||
await server.register(registerExternalGroupOrgRoleMappingRouter, { prefix: "/external-group-mappings" });
|
await server.register(registerExternalGroupOrgRoleMappingRouter, { prefix: "/external-group-mappings" });
|
||||||
|
|
||||||
await server.register(
|
await server.register(
|
||||||
async (appConnectionsRouter) => {
|
async (appConnectionRouter) => {
|
||||||
await appConnectionsRouter.register(registerAppConnectionRouter);
|
// register generic app connection endpoints
|
||||||
for await (const [app, router] of Object.entries(APP_CONNECTION_REGISTER_MAP)) {
|
await appConnectionRouter.register(registerAppConnectionRouter);
|
||||||
await appConnectionsRouter.register(router, { prefix: `/${app}` });
|
|
||||||
|
// register service specific endpoints (app-connections/aws, app-connections/github, etc.)
|
||||||
|
for await (const [app, router] of Object.entries(APP_CONNECTION_REGISTER_ROUTER_MAP)) {
|
||||||
|
await appConnectionRouter.register(router, { prefix: `/${app}` });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ prefix: "/app-connections" }
|
{ prefix: "/app-connections" }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await server.register(
|
||||||
|
async (secretSyncRouter) => {
|
||||||
|
// register generic secret sync endpoints
|
||||||
|
await secretSyncRouter.register(registerSecretSyncRouter);
|
||||||
|
|
||||||
|
// register service specific secret sync endpoints (secret-syncs/aws-parameter-store, secret-syncs/github, etc.)
|
||||||
|
for await (const [destination, router] of Object.entries(SECRET_SYNC_REGISTER_ROUTER_MAP)) {
|
||||||
|
await secretSyncRouter.register(router, { prefix: `/${destination}` });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ prefix: "/secret-syncs" }
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -131,7 +131,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
|||||||
body: z.object({
|
body: z.object({
|
||||||
app: z.string().trim().optional().describe(INTEGRATION.UPDATE.app),
|
app: z.string().trim().optional().describe(INTEGRATION.UPDATE.app),
|
||||||
appId: z.string().trim().optional().describe(INTEGRATION.UPDATE.appId),
|
appId: z.string().trim().optional().describe(INTEGRATION.UPDATE.appId),
|
||||||
isActive: z.boolean().describe(INTEGRATION.UPDATE.isActive),
|
isActive: z.boolean().optional().describe(INTEGRATION.UPDATE.isActive),
|
||||||
secretPath: z
|
secretPath: z
|
||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
|
@ -73,6 +73,40 @@ export const registerInviteOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/signup-resend",
|
||||||
|
config: {
|
||||||
|
rateLimit: inviteUserRateLimit
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
membershipId: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
signupToken: z
|
||||||
|
.object({
|
||||||
|
email: z.string(),
|
||||||
|
link: z.string()
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
return server.services.org.resendOrgMemberInvitation({
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
membershipId: req.body.membershipId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/verify",
|
url: "/verify",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { SecretSharingSchema } from "@app/db/schemas";
|
import { SecretSharingSchema } from "@app/db/schemas";
|
||||||
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { SecretSharingAccessType } from "@app/lib/types";
|
import { SecretSharingAccessType } from "@app/lib/types";
|
||||||
import {
|
import {
|
||||||
publicEndpointLimit,
|
publicEndpointLimit,
|
||||||
@ -88,6 +89,21 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
|
|||||||
orgId: req.permission?.orgId
|
orgId: req.permission?.orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (sharedSecret.secret?.orgId) {
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
orgId: sharedSecret.secret.orgId,
|
||||||
|
...req.auditLogInfo,
|
||||||
|
event: {
|
||||||
|
type: EventType.READ_SHARED_SECRET,
|
||||||
|
metadata: {
|
||||||
|
id: req.params.id,
|
||||||
|
name: sharedSecret.secret.name || undefined,
|
||||||
|
accessType: sharedSecret.secret.accessType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return sharedSecret;
|
return sharedSecret;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -151,6 +167,23 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
|
|||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
...req.body
|
...req.body
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
...req.auditLogInfo,
|
||||||
|
event: {
|
||||||
|
type: EventType.CREATE_SHARED_SECRET,
|
||||||
|
metadata: {
|
||||||
|
accessType: req.body.accessType,
|
||||||
|
expiresAt: req.body.expiresAt,
|
||||||
|
expiresAfterViews: req.body.expiresAfterViews,
|
||||||
|
name: req.body.name,
|
||||||
|
id: sharedSecret.id,
|
||||||
|
usingPassword: !!req.body.password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return { id: sharedSecret.id };
|
return { id: sharedSecret.id };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -181,6 +214,18 @@ export const registerSecretSharingRouter = async (server: FastifyZodProvider) =>
|
|||||||
sharedSecretId
|
sharedSecretId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
...req.auditLogInfo,
|
||||||
|
event: {
|
||||||
|
type: EventType.DELETE_SHARED_SECRET,
|
||||||
|
metadata: {
|
||||||
|
id: sharedSecretId,
|
||||||
|
name: deletedSharedSecret.name || undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return { ...deletedSharedSecret };
|
return { ...deletedSharedSecret };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
import {
|
||||||
|
AwsParameterStoreSyncSchema,
|
||||||
|
CreateAwsParameterStoreSyncSchema,
|
||||||
|
UpdateAwsParameterStoreSyncSchema
|
||||||
|
} from "@app/services/secret-sync/aws-parameter-store";
|
||||||
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
|
||||||
|
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
|
||||||
|
|
||||||
|
export const registerAwsParameterStoreSyncRouter = async (server: FastifyZodProvider) =>
|
||||||
|
registerSyncSecretsEndpoints({
|
||||||
|
destination: SecretSync.AWSParameterStore,
|
||||||
|
server,
|
||||||
|
responseSchema: AwsParameterStoreSyncSchema,
|
||||||
|
createSchema: CreateAwsParameterStoreSyncSchema,
|
||||||
|
updateSchema: UpdateAwsParameterStoreSyncSchema
|
||||||
|
});
|
@ -0,0 +1,13 @@
|
|||||||
|
import { CreateGitHubSyncSchema, GitHubSyncSchema, UpdateGitHubSyncSchema } from "@app/services/secret-sync/github";
|
||||||
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
|
||||||
|
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
|
||||||
|
|
||||||
|
export const registerGitHubSyncRouter = async (server: FastifyZodProvider) =>
|
||||||
|
registerSyncSecretsEndpoints({
|
||||||
|
destination: SecretSync.GitHub,
|
||||||
|
server,
|
||||||
|
responseSchema: GitHubSyncSchema,
|
||||||
|
createSchema: CreateGitHubSyncSchema,
|
||||||
|
updateSchema: UpdateGitHubSyncSchema
|
||||||
|
});
|
11
backend/src/server/routes/v1/secret-sync-routers/index.ts
Normal file
11
backend/src/server/routes/v1/secret-sync-routers/index.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
|
||||||
|
import { registerAwsParameterStoreSyncRouter } from "./aws-parameter-store-sync-router";
|
||||||
|
import { registerGitHubSyncRouter } from "./github-sync-router";
|
||||||
|
|
||||||
|
export * from "./secret-sync-router";
|
||||||
|
|
||||||
|
export const SECRET_SYNC_REGISTER_ROUTER_MAP: Record<SecretSync, (server: FastifyZodProvider) => Promise<void>> = {
|
||||||
|
[SecretSync.AWSParameterStore]: registerAwsParameterStoreSyncRouter,
|
||||||
|
[SecretSync.GitHub]: registerGitHubSyncRouter
|
||||||
|
};
|
@ -0,0 +1,408 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
|
import { SecretSyncs } from "@app/lib/api-docs";
|
||||||
|
import { startsWithVowel } from "@app/lib/fn";
|
||||||
|
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";
|
||||||
|
import { SecretSync, SecretSyncImportBehavior } from "@app/services/secret-sync/secret-sync-enums";
|
||||||
|
import { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps";
|
||||||
|
import { TSecretSync, TSecretSyncInput } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
|
export const registerSyncSecretsEndpoints = <T extends TSecretSync, I extends TSecretSyncInput>({
|
||||||
|
server,
|
||||||
|
destination,
|
||||||
|
createSchema,
|
||||||
|
updateSchema,
|
||||||
|
responseSchema
|
||||||
|
}: {
|
||||||
|
destination: SecretSync;
|
||||||
|
server: FastifyZodProvider;
|
||||||
|
createSchema: z.ZodType<{
|
||||||
|
name: string;
|
||||||
|
environment: string;
|
||||||
|
secretPath: string;
|
||||||
|
projectId: string;
|
||||||
|
connectionId: string;
|
||||||
|
destinationConfig: I["destinationConfig"];
|
||||||
|
syncOptions: I["syncOptions"];
|
||||||
|
description?: string | null;
|
||||||
|
isAutoSyncEnabled?: boolean;
|
||||||
|
}>;
|
||||||
|
updateSchema: z.ZodType<{
|
||||||
|
connectionId?: string;
|
||||||
|
name?: string;
|
||||||
|
environment?: string;
|
||||||
|
secretPath?: string;
|
||||||
|
destinationConfig?: I["destinationConfig"];
|
||||||
|
syncOptions?: I["syncOptions"];
|
||||||
|
description?: string | null;
|
||||||
|
isAutoSyncEnabled?: boolean;
|
||||||
|
}>;
|
||||||
|
responseSchema: z.ZodTypeAny;
|
||||||
|
}) => {
|
||||||
|
const destinationName = SECRET_SYNC_NAME_MAP[destination];
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: `/`,
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `List the ${destinationName} Syncs for the specified project.`,
|
||||||
|
querystring: z.object({
|
||||||
|
projectId: z.string().trim().min(1, "Project ID required").describe(SecretSyncs.LIST(destination).projectId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretSyncs: responseSchema.array() })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const {
|
||||||
|
query: { projectId }
|
||||||
|
} = req;
|
||||||
|
|
||||||
|
const secretSyncs = (await server.services.secretSync.listSecretSyncsByProjectId(
|
||||||
|
{ projectId, destination },
|
||||||
|
req.permission
|
||||||
|
)) as T[];
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.GET_SECRET_SYNCS,
|
||||||
|
metadata: {
|
||||||
|
destination,
|
||||||
|
count: secretSyncs.length,
|
||||||
|
syncIds: secretSyncs.map((connection) => connection.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secretSyncs };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/:syncId",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Get the specified ${destinationName} Sync by ID.`,
|
||||||
|
params: z.object({
|
||||||
|
syncId: z.string().uuid().describe(SecretSyncs.GET_BY_ID(destination).syncId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretSync: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { syncId } = req.params;
|
||||||
|
|
||||||
|
const secretSync = (await server.services.secretSync.findSecretSyncById(
|
||||||
|
{ syncId, destination },
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId: secretSync.projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.GET_SECRET_SYNC,
|
||||||
|
metadata: {
|
||||||
|
syncId,
|
||||||
|
destination
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secretSync };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: `/sync-name/:syncName`,
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Get the specified ${destinationName} Sync by name and project ID.`,
|
||||||
|
params: z.object({
|
||||||
|
syncName: z.string().trim().min(1, "Sync name required").describe(SecretSyncs.GET_BY_NAME(destination).syncName)
|
||||||
|
}),
|
||||||
|
querystring: z.object({
|
||||||
|
projectId: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(1, "Project ID required")
|
||||||
|
.describe(SecretSyncs.GET_BY_NAME(destination).projectId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretSync: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { syncName } = req.params;
|
||||||
|
const { projectId } = req.query;
|
||||||
|
|
||||||
|
const secretSync = (await server.services.secretSync.findSecretSyncByName(
|
||||||
|
{ syncName, projectId, destination },
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.GET_SECRET_SYNC,
|
||||||
|
metadata: {
|
||||||
|
syncId: secretSync.id,
|
||||||
|
destination
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secretSync };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Create ${
|
||||||
|
startsWithVowel(destinationName) ? "an" : "a"
|
||||||
|
} ${destinationName} Sync for the specified project environment.`,
|
||||||
|
body: createSchema,
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretSync: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const secretSync = (await server.services.secretSync.createSecretSync(
|
||||||
|
{ ...req.body, destination },
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId: secretSync.projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.CREATE_SECRET_SYNC,
|
||||||
|
metadata: {
|
||||||
|
syncId: secretSync.id,
|
||||||
|
destination,
|
||||||
|
...req.body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secretSync };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "PATCH",
|
||||||
|
url: "/:syncId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Update the specified ${destinationName} Sync.`,
|
||||||
|
params: z.object({
|
||||||
|
syncId: z.string().uuid().describe(SecretSyncs.UPDATE(destination).syncId)
|
||||||
|
}),
|
||||||
|
body: updateSchema,
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretSync: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { syncId } = req.params;
|
||||||
|
|
||||||
|
const secretSync = (await server.services.secretSync.updateSecretSync(
|
||||||
|
{ ...req.body, syncId, destination },
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId: secretSync.projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.UPDATE_SECRET_SYNC,
|
||||||
|
metadata: {
|
||||||
|
syncId,
|
||||||
|
destination,
|
||||||
|
...req.body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secretSync };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: `/:syncId`,
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Delete the specified ${destinationName} Sync.`,
|
||||||
|
params: z.object({
|
||||||
|
syncId: z.string().uuid().describe(SecretSyncs.DELETE(destination).syncId)
|
||||||
|
}),
|
||||||
|
querystring: z.object({
|
||||||
|
removeSecrets: z
|
||||||
|
.enum(["true", "false"])
|
||||||
|
.default("false")
|
||||||
|
.transform((value) => value === "true")
|
||||||
|
.describe(SecretSyncs.DELETE(destination).removeSecrets)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretSync: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { syncId } = req.params;
|
||||||
|
const { removeSecrets } = req.query;
|
||||||
|
|
||||||
|
const secretSync = (await server.services.secretSync.deleteSecretSync(
|
||||||
|
{ destination, syncId, removeSecrets },
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
event: {
|
||||||
|
type: EventType.DELETE_SECRET_SYNC,
|
||||||
|
metadata: {
|
||||||
|
destination,
|
||||||
|
syncId,
|
||||||
|
removeSecrets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secretSync };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/:syncId/sync-secrets",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Trigger a sync for the specified ${destinationName} Sync.`,
|
||||||
|
params: z.object({
|
||||||
|
syncId: z.string().uuid().describe(SecretSyncs.SYNC_SECRETS(destination).syncId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretSync: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { syncId } = req.params;
|
||||||
|
|
||||||
|
const secretSync = (await server.services.secretSync.triggerSecretSyncSyncSecretsById(
|
||||||
|
{
|
||||||
|
syncId,
|
||||||
|
destination,
|
||||||
|
auditLogInfo: req.auditLogInfo
|
||||||
|
},
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
return { secretSync };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/:syncId/import-secrets",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Import secrets from the specified ${destinationName} Sync destination.`,
|
||||||
|
params: z.object({
|
||||||
|
syncId: z.string().uuid().describe(SecretSyncs.IMPORT_SECRETS(destination).syncId)
|
||||||
|
}),
|
||||||
|
querystring: z.object({
|
||||||
|
importBehavior: z
|
||||||
|
.nativeEnum(SecretSyncImportBehavior)
|
||||||
|
.describe(SecretSyncs.IMPORT_SECRETS(destination).importBehavior)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretSync: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { syncId } = req.params;
|
||||||
|
const { importBehavior } = req.query;
|
||||||
|
|
||||||
|
const secretSync = (await server.services.secretSync.triggerSecretSyncImportSecretsById(
|
||||||
|
{
|
||||||
|
syncId,
|
||||||
|
destination,
|
||||||
|
importBehavior
|
||||||
|
},
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
return { secretSync };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/:syncId/remove-secrets",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Remove previously synced secrets from the specified ${destinationName} Sync destination.`,
|
||||||
|
params: z.object({
|
||||||
|
syncId: z.string().uuid().describe(SecretSyncs.REMOVE_SECRETS(destination).syncId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretSync: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { syncId } = req.params;
|
||||||
|
|
||||||
|
const secretSync = (await server.services.secretSync.triggerSecretSyncRemoveSecretsById(
|
||||||
|
{
|
||||||
|
syncId,
|
||||||
|
destination
|
||||||
|
},
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
return { secretSync };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@ -0,0 +1,82 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
|
import { SecretSyncs } from "@app/lib/api-docs";
|
||||||
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
import {
|
||||||
|
AwsParameterStoreSyncListItemSchema,
|
||||||
|
AwsParameterStoreSyncSchema
|
||||||
|
} from "@app/services/secret-sync/aws-parameter-store";
|
||||||
|
import { GitHubSyncListItemSchema, GitHubSyncSchema } from "@app/services/secret-sync/github";
|
||||||
|
|
||||||
|
const SecretSyncSchema = z.discriminatedUnion("destination", [AwsParameterStoreSyncSchema, GitHubSyncSchema]);
|
||||||
|
|
||||||
|
const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
||||||
|
AwsParameterStoreSyncListItemSchema,
|
||||||
|
GitHubSyncListItemSchema
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const registerSecretSyncRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/options",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: "List the available Secret Sync Options.",
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
secretSyncOptions: SecretSyncOptionsSchema.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: () => {
|
||||||
|
const secretSyncOptions = server.services.secretSync.listSecretSyncOptions();
|
||||||
|
return { secretSyncOptions };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: "List all the Secret Syncs for the specified project.",
|
||||||
|
querystring: z.object({
|
||||||
|
projectId: z.string().trim().min(1, "Project ID required").describe(SecretSyncs.LIST().projectId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretSyncs: SecretSyncSchema.array() })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const {
|
||||||
|
query: { projectId },
|
||||||
|
permission
|
||||||
|
} = req;
|
||||||
|
|
||||||
|
const secretSyncs = await server.services.secretSync.listSecretSyncsByProjectId({ projectId }, permission);
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.GET_SECRET_SYNCS,
|
||||||
|
metadata: {
|
||||||
|
syncIds: secretSyncs.map((sync) => sync.id),
|
||||||
|
count: secretSyncs.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secretSyncs };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@ -16,7 +16,7 @@ import { ProjectUserMembershipTemporaryMode } from "@app/services/project-member
|
|||||||
export const registerGroupProjectRouter = async (server: FastifyZodProvider) => {
|
export const registerGroupProjectRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/:projectId/groups/:groupId",
|
url: "/:projectId/groups/:groupIdOrName",
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
config: {
|
config: {
|
||||||
rateLimit: writeLimit
|
rateLimit: writeLimit
|
||||||
@ -30,7 +30,7 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().trim().describe(PROJECTS.ADD_GROUP_TO_PROJECT.projectId),
|
projectId: z.string().trim().describe(PROJECTS.ADD_GROUP_TO_PROJECT.projectId),
|
||||||
groupId: z.string().trim().describe(PROJECTS.ADD_GROUP_TO_PROJECT.groupId)
|
groupIdOrName: z.string().trim().describe(PROJECTS.ADD_GROUP_TO_PROJECT.groupIdOrName)
|
||||||
}),
|
}),
|
||||||
body: z
|
body: z
|
||||||
.object({
|
.object({
|
||||||
@ -76,7 +76,7 @@ export const registerGroupProjectRouter = async (server: FastifyZodProvider) =>
|
|||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
roles: req.body.roles || [{ role: req.body.role }],
|
roles: req.body.roles || [{ role: req.body.role }],
|
||||||
projectId: req.params.projectId,
|
projectId: req.params.projectId,
|
||||||
groupId: req.params.groupId
|
groupIdOrName: req.params.groupIdOrName
|
||||||
});
|
});
|
||||||
|
|
||||||
return { groupMembership };
|
return { groupMembership };
|
||||||
|
@ -2,3 +2,50 @@ export enum AppConnection {
|
|||||||
GitHub = "github",
|
GitHub = "github",
|
||||||
AWS = "aws"
|
AWS = "aws"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum AWSRegion {
|
||||||
|
// US
|
||||||
|
US_EAST_1 = "us-east-1", // N. Virginia
|
||||||
|
US_EAST_2 = "us-east-2", // Ohio
|
||||||
|
US_WEST_1 = "us-west-1", // N. California
|
||||||
|
US_WEST_2 = "us-west-2", // Oregon
|
||||||
|
|
||||||
|
// GovCloud
|
||||||
|
US_GOV_EAST_1 = "us-gov-east-1", // US-East
|
||||||
|
US_GOV_WEST_1 = "us-gov-west-1", // US-West
|
||||||
|
|
||||||
|
// Africa
|
||||||
|
AF_SOUTH_1 = "af-south-1", // Cape Town
|
||||||
|
|
||||||
|
// Asia Pacific
|
||||||
|
AP_EAST_1 = "ap-east-1", // Hong Kong
|
||||||
|
AP_SOUTH_1 = "ap-south-1", // Mumbai
|
||||||
|
AP_SOUTH_2 = "ap-south-2", // Hyderabad
|
||||||
|
AP_NORTHEAST_1 = "ap-northeast-1", // Tokyo
|
||||||
|
AP_NORTHEAST_2 = "ap-northeast-2", // Seoul
|
||||||
|
AP_NORTHEAST_3 = "ap-northeast-3", // Osaka
|
||||||
|
AP_SOUTHEAST_1 = "ap-southeast-1", // Singapore
|
||||||
|
AP_SOUTHEAST_2 = "ap-southeast-2", // Sydney
|
||||||
|
AP_SOUTHEAST_3 = "ap-southeast-3", // Jakarta
|
||||||
|
AP_SOUTHEAST_4 = "ap-southeast-4", // Melbourne
|
||||||
|
|
||||||
|
// Canada
|
||||||
|
CA_CENTRAL_1 = "ca-central-1", // Central
|
||||||
|
|
||||||
|
// Europe
|
||||||
|
EU_CENTRAL_1 = "eu-central-1", // Frankfurt
|
||||||
|
EU_CENTRAL_2 = "eu-central-2", // Zurich
|
||||||
|
EU_WEST_1 = "eu-west-1", // Ireland
|
||||||
|
EU_WEST_2 = "eu-west-2", // London
|
||||||
|
EU_WEST_3 = "eu-west-3", // Paris
|
||||||
|
EU_SOUTH_1 = "eu-south-1", // Milan
|
||||||
|
EU_SOUTH_2 = "eu-south-2", // Spain
|
||||||
|
EU_NORTH_1 = "eu-north-1", // Stockholm
|
||||||
|
|
||||||
|
// Middle East
|
||||||
|
ME_SOUTH_1 = "me-south-1", // Bahrain
|
||||||
|
ME_CENTRAL_1 = "me-central-1", // UAE
|
||||||
|
|
||||||
|
// South America
|
||||||
|
SA_EAST_1 = "sa-east-1" // Sao Paulo
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { TAppConnections } from "@app/db/schemas/app-connections";
|
||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
import { TAppConnectionServiceFactoryDep } from "@app/services/app-connection/app-connection-service";
|
import { TAppConnectionServiceFactoryDep } from "@app/services/app-connection/app-connection-service";
|
||||||
import { TAppConnection, TAppConnectionConfig } from "@app/services/app-connection/app-connection-types";
|
import { TAppConnection, TAppConnectionConfig } from "@app/services/app-connection/app-connection-types";
|
||||||
@ -64,9 +65,8 @@ export const validateAppConnectionCredentials = async (
|
|||||||
): Promise<TAppConnection["credentials"]> => {
|
): Promise<TAppConnection["credentials"]> => {
|
||||||
const { app } = appConnection;
|
const { app } = appConnection;
|
||||||
switch (app) {
|
switch (app) {
|
||||||
case AppConnection.AWS: {
|
case AppConnection.AWS:
|
||||||
return validateAwsConnectionCredentials(appConnection);
|
return validateAwsConnectionCredentials(appConnection);
|
||||||
}
|
|
||||||
case AppConnection.GitHub:
|
case AppConnection.GitHub:
|
||||||
return validateGitHubConnectionCredentials(appConnection);
|
return validateGitHubConnectionCredentials(appConnection);
|
||||||
default:
|
default:
|
||||||
@ -90,3 +90,17 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
|
|||||||
throw new Error(`Unhandled App Connection Method: ${method}`);
|
throw new Error(`Unhandled App Connection Method: ${method}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const decryptAppConnection = async (
|
||||||
|
appConnection: TAppConnections,
|
||||||
|
kmsService: TAppConnectionServiceFactoryDep["kmsService"]
|
||||||
|
) => {
|
||||||
|
return {
|
||||||
|
...appConnection,
|
||||||
|
credentials: await decryptAppConnectionCredentials({
|
||||||
|
encryptedCredentials: appConnection.encryptedCredentials,
|
||||||
|
orgId: appConnection.orgId,
|
||||||
|
kmsService
|
||||||
|
})
|
||||||
|
} as TAppConnection;
|
||||||
|
};
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
|
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { OrgPermissionAppConnectionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
|
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors";
|
||||||
import { DiscriminativePick, OrgServiceActor } from "@app/lib/types";
|
import { DiscriminativePick, OrgServiceActor } from "@app/lib/types";
|
||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
import {
|
import {
|
||||||
decryptAppConnectionCredentials,
|
decryptAppConnection,
|
||||||
encryptAppConnectionCredentials,
|
encryptAppConnectionCredentials,
|
||||||
getAppConnectionMethodName,
|
getAppConnectionMethodName,
|
||||||
listAppConnectionOptions,
|
listAppConnectionOptions,
|
||||||
@ -23,6 +22,7 @@ import {
|
|||||||
} from "@app/services/app-connection/app-connection-types";
|
} from "@app/services/app-connection/app-connection-types";
|
||||||
import { ValidateAwsConnectionCredentialsSchema } from "@app/services/app-connection/aws";
|
import { ValidateAwsConnectionCredentialsSchema } from "@app/services/app-connection/aws";
|
||||||
import { ValidateGitHubConnectionCredentialsSchema } from "@app/services/app-connection/github";
|
import { ValidateGitHubConnectionCredentialsSchema } from "@app/services/app-connection/github";
|
||||||
|
import { githubConnectionService } from "@app/services/app-connection/github/github-connection-service";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
|
||||||
import { TAppConnectionDALFactory } from "./app-connection-dal";
|
import { TAppConnectionDALFactory } from "./app-connection-dal";
|
||||||
@ -31,7 +31,6 @@ export type TAppConnectionServiceFactoryDep = {
|
|||||||
appConnectionDAL: TAppConnectionDALFactory;
|
appConnectionDAL: TAppConnectionDALFactory;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">; // TODO: remove once launched
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TAppConnectionServiceFactory = ReturnType<typeof appConnectionServiceFactory>;
|
export type TAppConnectionServiceFactory = ReturnType<typeof appConnectionServiceFactory>;
|
||||||
@ -44,19 +43,9 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
|
|||||||
export const appConnectionServiceFactory = ({
|
export const appConnectionServiceFactory = ({
|
||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
kmsService,
|
kmsService
|
||||||
licenseService
|
|
||||||
}: TAppConnectionServiceFactoryDep) => {
|
}: TAppConnectionServiceFactoryDep) => {
|
||||||
// app connections are disabled for public until launch
|
|
||||||
const checkAppServicesAvailability = async (orgId: string) => {
|
|
||||||
const subscription = await licenseService.getPlan(orgId);
|
|
||||||
|
|
||||||
if (!subscription.appConnections) throw new BadRequestError({ message: "App Connections are not available yet." });
|
|
||||||
};
|
|
||||||
|
|
||||||
const listAppConnectionsByOrg = async (actor: OrgServiceActor, app?: AppConnection) => {
|
const listAppConnectionsByOrg = async (actor: OrgServiceActor, app?: AppConnection) => {
|
||||||
await checkAppServicesAvailability(actor.orgId);
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor.type,
|
actor.type,
|
||||||
actor.id,
|
actor.id,
|
||||||
@ -65,7 +54,10 @@ export const appConnectionServiceFactory = ({
|
|||||||
actor.orgId
|
actor.orgId
|
||||||
);
|
);
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.AppConnections);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionAppConnectionActions.Read,
|
||||||
|
OrgPermissionSubjects.AppConnections
|
||||||
|
);
|
||||||
|
|
||||||
const appConnections = await appConnectionDAL.find(
|
const appConnections = await appConnectionDAL.find(
|
||||||
app
|
app
|
||||||
@ -78,24 +70,11 @@ export const appConnectionServiceFactory = ({
|
|||||||
return Promise.all(
|
return Promise.all(
|
||||||
appConnections
|
appConnections
|
||||||
.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()))
|
.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()))
|
||||||
.map(async ({ encryptedCredentials, ...connection }) => {
|
.map((appConnection) => decryptAppConnection(appConnection, kmsService))
|
||||||
const credentials = await decryptAppConnectionCredentials({
|
|
||||||
encryptedCredentials,
|
|
||||||
kmsService,
|
|
||||||
orgId: connection.orgId
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
...connection,
|
|
||||||
credentials
|
|
||||||
} as TAppConnection;
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const findAppConnectionById = async (app: AppConnection, connectionId: string, actor: OrgServiceActor) => {
|
const findAppConnectionById = async (app: AppConnection, connectionId: string, actor: OrgServiceActor) => {
|
||||||
await checkAppServicesAvailability(actor.orgId);
|
|
||||||
|
|
||||||
const appConnection = await appConnectionDAL.findById(connectionId);
|
const appConnection = await appConnectionDAL.findById(connectionId);
|
||||||
|
|
||||||
if (!appConnection) throw new NotFoundError({ message: `Could not find App Connection with ID ${connectionId}` });
|
if (!appConnection) throw new NotFoundError({ message: `Could not find App Connection with ID ${connectionId}` });
|
||||||
@ -108,24 +87,18 @@ export const appConnectionServiceFactory = ({
|
|||||||
appConnection.orgId
|
appConnection.orgId
|
||||||
);
|
);
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.AppConnections);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionAppConnectionActions.Read,
|
||||||
|
OrgPermissionSubjects.AppConnections
|
||||||
|
);
|
||||||
|
|
||||||
if (appConnection.app !== app)
|
if (appConnection.app !== app)
|
||||||
throw new BadRequestError({ message: `App Connection with ID ${connectionId} is not for App "${app}"` });
|
throw new BadRequestError({ message: `App Connection with ID ${connectionId} is not for App "${app}"` });
|
||||||
|
|
||||||
return {
|
return decryptAppConnection(appConnection, kmsService);
|
||||||
...appConnection,
|
|
||||||
credentials: await decryptAppConnectionCredentials({
|
|
||||||
encryptedCredentials: appConnection.encryptedCredentials,
|
|
||||||
orgId: appConnection.orgId,
|
|
||||||
kmsService
|
|
||||||
})
|
|
||||||
} as TAppConnection;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const findAppConnectionByName = async (app: AppConnection, connectionName: string, actor: OrgServiceActor) => {
|
const findAppConnectionByName = async (app: AppConnection, connectionName: string, actor: OrgServiceActor) => {
|
||||||
await checkAppServicesAvailability(actor.orgId);
|
|
||||||
|
|
||||||
const appConnection = await appConnectionDAL.findOne({ name: connectionName, orgId: actor.orgId });
|
const appConnection = await appConnectionDAL.findOne({ name: connectionName, orgId: actor.orgId });
|
||||||
|
|
||||||
if (!appConnection)
|
if (!appConnection)
|
||||||
@ -139,27 +112,21 @@ export const appConnectionServiceFactory = ({
|
|||||||
appConnection.orgId
|
appConnection.orgId
|
||||||
);
|
);
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.AppConnections);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionAppConnectionActions.Read,
|
||||||
|
OrgPermissionSubjects.AppConnections
|
||||||
|
);
|
||||||
|
|
||||||
if (appConnection.app !== app)
|
if (appConnection.app !== app)
|
||||||
throw new BadRequestError({ message: `App Connection with name ${connectionName} is not for App "${app}"` });
|
throw new BadRequestError({ message: `App Connection with name ${connectionName} is not for App "${app}"` });
|
||||||
|
|
||||||
return {
|
return decryptAppConnection(appConnection, kmsService);
|
||||||
...appConnection,
|
|
||||||
credentials: await decryptAppConnectionCredentials({
|
|
||||||
encryptedCredentials: appConnection.encryptedCredentials,
|
|
||||||
orgId: appConnection.orgId,
|
|
||||||
kmsService
|
|
||||||
})
|
|
||||||
} as TAppConnection;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createAppConnection = async (
|
const createAppConnection = async (
|
||||||
{ method, app, credentials, ...params }: TCreateAppConnectionDTO,
|
{ method, app, credentials, ...params }: TCreateAppConnectionDTO,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
) => {
|
) => {
|
||||||
await checkAppServicesAvailability(actor.orgId);
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission } = await permissionService.getOrgPermission(
|
||||||
actor.type,
|
actor.type,
|
||||||
actor.id,
|
actor.id,
|
||||||
@ -168,7 +135,10 @@ export const appConnectionServiceFactory = ({
|
|||||||
actor.orgId
|
actor.orgId
|
||||||
);
|
);
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.AppConnections);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionAppConnectionActions.Create,
|
||||||
|
OrgPermissionSubjects.AppConnections
|
||||||
|
);
|
||||||
|
|
||||||
const appConnection = await appConnectionDAL.transaction(async (tx) => {
|
const appConnection = await appConnectionDAL.transaction(async (tx) => {
|
||||||
const isConflictingName = Boolean(
|
const isConflictingName = Boolean(
|
||||||
@ -216,15 +186,13 @@ export const appConnectionServiceFactory = ({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return appConnection;
|
return appConnection as TAppConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateAppConnection = async (
|
const updateAppConnection = async (
|
||||||
{ connectionId, credentials, ...params }: TUpdateAppConnectionDTO,
|
{ connectionId, credentials, ...params }: TUpdateAppConnectionDTO,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
) => {
|
) => {
|
||||||
await checkAppServicesAvailability(actor.orgId);
|
|
||||||
|
|
||||||
const appConnection = await appConnectionDAL.findById(connectionId);
|
const appConnection = await appConnectionDAL.findById(connectionId);
|
||||||
|
|
||||||
if (!appConnection) throw new NotFoundError({ message: `Could not find App Connection with ID ${connectionId}` });
|
if (!appConnection) throw new NotFoundError({ message: `Could not find App Connection with ID ${connectionId}` });
|
||||||
@ -237,7 +205,10 @@ export const appConnectionServiceFactory = ({
|
|||||||
appConnection.orgId
|
appConnection.orgId
|
||||||
);
|
);
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.AppConnections);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionAppConnectionActions.Edit,
|
||||||
|
OrgPermissionSubjects.AppConnections
|
||||||
|
);
|
||||||
|
|
||||||
const updatedAppConnection = await appConnectionDAL.transaction(async (tx) => {
|
const updatedAppConnection = await appConnectionDAL.transaction(async (tx) => {
|
||||||
if (params.name && appConnection.name !== params.name) {
|
if (params.name && appConnection.name !== params.name) {
|
||||||
@ -304,19 +275,10 @@ export const appConnectionServiceFactory = ({
|
|||||||
return updatedConnection;
|
return updatedConnection;
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return decryptAppConnection(updatedAppConnection, kmsService);
|
||||||
...updatedAppConnection,
|
|
||||||
credentials: await decryptAppConnectionCredentials({
|
|
||||||
encryptedCredentials: updatedAppConnection.encryptedCredentials,
|
|
||||||
orgId: updatedAppConnection.orgId,
|
|
||||||
kmsService
|
|
||||||
})
|
|
||||||
} as TAppConnection;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteAppConnection = async (app: AppConnection, connectionId: string, actor: OrgServiceActor) => {
|
const deleteAppConnection = async (app: AppConnection, connectionId: string, actor: OrgServiceActor) => {
|
||||||
await checkAppServicesAvailability(actor.orgId);
|
|
||||||
|
|
||||||
const appConnection = await appConnectionDAL.findById(connectionId);
|
const appConnection = await appConnectionDAL.findById(connectionId);
|
||||||
|
|
||||||
if (!appConnection) throw new NotFoundError({ message: `Could not find App Connection with ID ${connectionId}` });
|
if (!appConnection) throw new NotFoundError({ message: `Could not find App Connection with ID ${connectionId}` });
|
||||||
@ -329,23 +291,85 @@ export const appConnectionServiceFactory = ({
|
|||||||
appConnection.orgId
|
appConnection.orgId
|
||||||
);
|
);
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.AppConnections);
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
OrgPermissionAppConnectionActions.Delete,
|
||||||
|
OrgPermissionSubjects.AppConnections
|
||||||
|
);
|
||||||
|
|
||||||
if (appConnection.app !== app)
|
if (appConnection.app !== app)
|
||||||
throw new BadRequestError({ message: `App Connection with ID ${connectionId} is not for App "${app}"` });
|
throw new BadRequestError({ message: `App Connection with ID ${connectionId} is not for App "${app}"` });
|
||||||
|
|
||||||
// TODO: specify delete error message if due to existing dependencies
|
// TODO (scott): add option to delete all dependencies
|
||||||
|
|
||||||
const deletedAppConnection = await appConnectionDAL.deleteById(connectionId);
|
try {
|
||||||
|
const deletedAppConnection = await appConnectionDAL.deleteById(connectionId);
|
||||||
|
|
||||||
return {
|
return await decryptAppConnection(deletedAppConnection, kmsService);
|
||||||
...deletedAppConnection,
|
} catch (err) {
|
||||||
credentials: await decryptAppConnectionCredentials({
|
if (err instanceof DatabaseError && (err.error as { code: string })?.code === "23503") {
|
||||||
encryptedCredentials: deletedAppConnection.encryptedCredentials,
|
throw new BadRequestError({
|
||||||
orgId: deletedAppConnection.orgId,
|
message:
|
||||||
kmsService
|
"Cannot delete App Connection with existing connections. Remove all existing connections and try again."
|
||||||
})
|
});
|
||||||
} as TAppConnection;
|
}
|
||||||
|
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const connectAppConnectionById = async <T extends TAppConnection>(
|
||||||
|
app: AppConnection,
|
||||||
|
connectionId: string,
|
||||||
|
actor: OrgServiceActor
|
||||||
|
) => {
|
||||||
|
const appConnection = await appConnectionDAL.findById(connectionId);
|
||||||
|
|
||||||
|
if (!appConnection) throw new NotFoundError({ message: `Could not find App Connection with ID ${connectionId}` });
|
||||||
|
|
||||||
|
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||||
|
actor.type,
|
||||||
|
actor.id,
|
||||||
|
appConnection.orgId,
|
||||||
|
actor.authMethod,
|
||||||
|
actor.orgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(orgPermission).throwUnlessCan(
|
||||||
|
OrgPermissionAppConnectionActions.Connect,
|
||||||
|
subject(OrgPermissionSubjects.AppConnections, { connectionId: appConnection.id })
|
||||||
|
);
|
||||||
|
|
||||||
|
if (appConnection.app !== app)
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: `${
|
||||||
|
APP_CONNECTION_NAME_MAP[appConnection.app as AppConnection]
|
||||||
|
} Connection with ID ${connectionId} cannot be used to connect to ${APP_CONNECTION_NAME_MAP[app]}`
|
||||||
|
});
|
||||||
|
|
||||||
|
const connection = await decryptAppConnection(appConnection, kmsService);
|
||||||
|
|
||||||
|
return connection as T;
|
||||||
|
};
|
||||||
|
|
||||||
|
const listAvailableAppConnectionsForUser = async (app: AppConnection, actor: OrgServiceActor) => {
|
||||||
|
const { permission: orgPermission } = await permissionService.getOrgPermission(
|
||||||
|
actor.type,
|
||||||
|
actor.id,
|
||||||
|
actor.orgId,
|
||||||
|
actor.authMethod,
|
||||||
|
actor.orgId
|
||||||
|
);
|
||||||
|
|
||||||
|
const appConnections = await appConnectionDAL.find({ app, orgId: actor.orgId });
|
||||||
|
|
||||||
|
const availableConnections = appConnections.filter((connection) =>
|
||||||
|
orgPermission.can(
|
||||||
|
OrgPermissionAppConnectionActions.Connect,
|
||||||
|
subject(OrgPermissionSubjects.AppConnections, { connectionId: connection.id })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return availableConnections as Omit<TAppConnection, "credentials">[];
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -355,6 +379,9 @@ export const appConnectionServiceFactory = ({
|
|||||||
findAppConnectionByName,
|
findAppConnectionByName,
|
||||||
createAppConnection,
|
createAppConnection,
|
||||||
updateAppConnection,
|
updateAppConnection,
|
||||||
deleteAppConnection
|
deleteAppConnection,
|
||||||
|
connectAppConnectionById,
|
||||||
|
listAvailableAppConnectionsForUser,
|
||||||
|
github: githubConnectionService(connectAppConnectionById)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@ import { randomUUID } from "crypto";
|
|||||||
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, InternalServerError } from "@app/lib/errors";
|
import { BadRequestError, InternalServerError } from "@app/lib/errors";
|
||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
import { AppConnection, AWSRegion } from "@app/services/app-connection/app-connection-enums";
|
||||||
|
|
||||||
import { AwsConnectionMethod } from "./aws-connection-enums";
|
import { AwsConnectionMethod } from "./aws-connection-enums";
|
||||||
import { TAwsConnectionConfig } from "./aws-connection-types";
|
import { TAwsConnectionConfig } from "./aws-connection-types";
|
||||||
@ -20,7 +20,7 @@ export const getAwsAppConnectionListItem = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAwsConnectionConfig = async (appConnection: TAwsConnectionConfig, region = "us-east-1") => {
|
export const getAwsConnectionConfig = async (appConnection: TAwsConnectionConfig, region = AWSRegion.US_EAST_1) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
|
|
||||||
let accessKeyId: string;
|
let accessKeyId: string;
|
||||||
|
@ -38,11 +38,11 @@ export const AwsConnectionSchema = z.intersection(
|
|||||||
export const SanitizedAwsConnectionSchema = z.discriminatedUnion("method", [
|
export const SanitizedAwsConnectionSchema = z.discriminatedUnion("method", [
|
||||||
BaseAwsConnectionSchema.extend({
|
BaseAwsConnectionSchema.extend({
|
||||||
method: z.literal(AwsConnectionMethod.AssumeRole),
|
method: z.literal(AwsConnectionMethod.AssumeRole),
|
||||||
credentials: AwsConnectionAssumeRoleCredentialsSchema.omit({ roleArn: true })
|
credentials: AwsConnectionAssumeRoleCredentialsSchema.pick({})
|
||||||
}),
|
}),
|
||||||
BaseAwsConnectionSchema.extend({
|
BaseAwsConnectionSchema.extend({
|
||||||
method: z.literal(AwsConnectionMethod.AccessKey),
|
method: z.literal(AwsConnectionMethod.AccessKey),
|
||||||
credentials: AwsConnectionAccessTokenCredentialsSchema.omit({ secretAccessKey: true })
|
credentials: AwsConnectionAccessTokenCredentialsSchema.pick({ accessKeyId: true })
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ export const UpdateAwsConnectionSchema = z
|
|||||||
export const AwsConnectionListItemSchema = z.object({
|
export const AwsConnectionListItemSchema = z.object({
|
||||||
name: z.literal("AWS"),
|
name: z.literal("AWS"),
|
||||||
app: z.literal(AppConnection.AWS),
|
app: z.literal(AppConnection.AWS),
|
||||||
// the below is preferable but currently breaks mintlify
|
// the below is preferable but currently breaks with our zod to json schema parser
|
||||||
// methods: z.tuple([z.literal(AwsConnectionMethod.AssumeRole), z.literal(AwsConnectionMethod.AccessKey)]),
|
// methods: z.tuple([z.literal(AwsConnectionMethod.AssumeRole), z.literal(AwsConnectionMethod.AccessKey)]),
|
||||||
methods: z.nativeEnum(AwsConnectionMethod).array(),
|
methods: z.nativeEnum(AwsConnectionMethod).array(),
|
||||||
accessKeyId: z.string().optional()
|
accessKeyId: z.string().optional()
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { createAppAuth } from "@octokit/auth-app";
|
||||||
|
import { Octokit } from "@octokit/rest";
|
||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
@ -8,7 +10,7 @@ import { IntegrationUrls } from "@app/services/integration-auth/integration-list
|
|||||||
|
|
||||||
import { AppConnection } from "../app-connection-enums";
|
import { AppConnection } from "../app-connection-enums";
|
||||||
import { GitHubConnectionMethod } from "./github-connection-enums";
|
import { GitHubConnectionMethod } from "./github-connection-enums";
|
||||||
import { TGitHubConnectionConfig } from "./github-connection-types";
|
import { TGitHubConnection, TGitHubConnectionConfig } from "./github-connection-types";
|
||||||
|
|
||||||
export const getGitHubConnectionListItem = () => {
|
export const getGitHubConnectionListItem = () => {
|
||||||
const { INF_APP_CONNECTION_GITHUB_OAUTH_CLIENT_ID, INF_APP_CONNECTION_GITHUB_APP_SLUG } = getConfig();
|
const { INF_APP_CONNECTION_GITHUB_OAUTH_CLIENT_ID, INF_APP_CONNECTION_GITHUB_APP_SLUG } = getConfig();
|
||||||
@ -22,10 +24,131 @@ export const getGitHubConnectionListItem = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getGitHubClient = (appConnection: TGitHubConnection) => {
|
||||||
|
const appCfg = getConfig();
|
||||||
|
|
||||||
|
const { method, credentials } = appConnection;
|
||||||
|
|
||||||
|
let client: Octokit;
|
||||||
|
|
||||||
|
switch (method) {
|
||||||
|
case GitHubConnectionMethod.App:
|
||||||
|
if (!appCfg.INF_APP_CONNECTION_GITHUB_APP_ID || !appCfg.INF_APP_CONNECTION_GITHUB_APP_PRIVATE_KEY) {
|
||||||
|
throw new InternalServerError({
|
||||||
|
message: `GitHub ${getAppConnectionMethodName(method).replace(
|
||||||
|
"GitHub",
|
||||||
|
""
|
||||||
|
)} environment variables have not been configured`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
client = new Octokit({
|
||||||
|
authStrategy: createAppAuth,
|
||||||
|
auth: {
|
||||||
|
appId: appCfg.INF_APP_CONNECTION_GITHUB_APP_ID,
|
||||||
|
privateKey: appCfg.INF_APP_CONNECTION_GITHUB_APP_PRIVATE_KEY,
|
||||||
|
installationId: credentials.installationId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case GitHubConnectionMethod.OAuth:
|
||||||
|
client = new Octokit({
|
||||||
|
auth: credentials.accessToken
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InternalServerError({
|
||||||
|
message: `Unhandled GitHub connection method: ${method as GitHubConnectionMethod}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return client;
|
||||||
|
};
|
||||||
|
|
||||||
|
type GitHubOrganization = {
|
||||||
|
login: string;
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type GitHubRepository = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
owner: GitHubOrganization;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getGitHubRepositories = async (appConnection: TGitHubConnection) => {
|
||||||
|
const client = getGitHubClient(appConnection);
|
||||||
|
|
||||||
|
let repositories: GitHubRepository[];
|
||||||
|
|
||||||
|
switch (appConnection.method) {
|
||||||
|
case GitHubConnectionMethod.App:
|
||||||
|
repositories = await client.paginate("GET /installation/repositories");
|
||||||
|
break;
|
||||||
|
case GitHubConnectionMethod.OAuth:
|
||||||
|
default:
|
||||||
|
repositories = (await client.paginate("GET /user/repos")).filter((repo) => repo.permissions?.admin);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return repositories;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getGitHubOrganizations = async (appConnection: TGitHubConnection) => {
|
||||||
|
const client = getGitHubClient(appConnection);
|
||||||
|
|
||||||
|
let organizations: GitHubOrganization[];
|
||||||
|
|
||||||
|
switch (appConnection.method) {
|
||||||
|
case GitHubConnectionMethod.App: {
|
||||||
|
const installationRepositories = await client.paginate("GET /installation/repositories");
|
||||||
|
|
||||||
|
const organizationMap: Record<string, GitHubOrganization> = {};
|
||||||
|
|
||||||
|
installationRepositories.forEach((repo) => {
|
||||||
|
if (repo.owner.type === "Organization") {
|
||||||
|
organizationMap[repo.owner.id] = repo.owner;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
organizations = Object.values(organizationMap);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GitHubConnectionMethod.OAuth:
|
||||||
|
default:
|
||||||
|
organizations = await client.paginate("GET /user/orgs");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return organizations;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getGitHubEnvironments = async (appConnection: TGitHubConnection, owner: string, repo: string) => {
|
||||||
|
const client = getGitHubClient(appConnection);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const environments = await client.paginate("GET /repos/{owner}/{repo}/environments", {
|
||||||
|
owner,
|
||||||
|
repo
|
||||||
|
});
|
||||||
|
|
||||||
|
return environments;
|
||||||
|
} catch (e) {
|
||||||
|
// repo doesn't have envs
|
||||||
|
if ((e as { status: number }).status === 404) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
type TokenRespData = {
|
type TokenRespData = {
|
||||||
access_token: string;
|
access_token: string;
|
||||||
scope: string;
|
scope: string;
|
||||||
token_type: string;
|
token_type: string;
|
||||||
|
error?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const validateGitHubConnectionCredentials = async (config: TGitHubConnectionConfig) => {
|
export const validateGitHubConnectionCredentials = async (config: TGitHubConnectionConfig) => {
|
||||||
@ -53,7 +176,10 @@ export const validateGitHubConnectionCredentials = async (config: TGitHubConnect
|
|||||||
|
|
||||||
if (!clientId || !clientSecret) {
|
if (!clientId || !clientSecret) {
|
||||||
throw new InternalServerError({
|
throw new InternalServerError({
|
||||||
message: `GitHub ${getAppConnectionMethodName(method)} environment variables have not been configured`
|
message: `GitHub ${getAppConnectionMethodName(method).replace(
|
||||||
|
"GitHub",
|
||||||
|
""
|
||||||
|
)} environment variables have not been configured`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +191,7 @@ export const validateGitHubConnectionCredentials = async (config: TGitHubConnect
|
|||||||
client_id: clientId,
|
client_id: clientId,
|
||||||
client_secret: clientSecret,
|
client_secret: clientSecret,
|
||||||
code: credentials.code,
|
code: credentials.code,
|
||||||
redirect_uri: `${SITE_URL}/app-connections/github/oauth/callback`
|
redirect_uri: `${SITE_URL}/organization/app-connections/github/oauth/callback`
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
@ -90,6 +216,8 @@ export const validateGitHubConnectionCredentials = async (config: TGitHubConnect
|
|||||||
id: number;
|
id: number;
|
||||||
account: {
|
account: {
|
||||||
login: string;
|
login: string;
|
||||||
|
type: string;
|
||||||
|
id: number;
|
||||||
};
|
};
|
||||||
}[];
|
}[];
|
||||||
}>(IntegrationUrls.GITHUB_USER_INSTALLATIONS, {
|
}>(IntegrationUrls.GITHUB_USER_INSTALLATIONS, {
|
||||||
@ -111,10 +239,13 @@ export const validateGitHubConnectionCredentials = async (config: TGitHubConnect
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tokenResp.data.access_token) {
|
||||||
|
throw new InternalServerError({ message: `Missing access token: ${tokenResp.data.error}` });
|
||||||
|
}
|
||||||
|
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case GitHubConnectionMethod.App:
|
case GitHubConnectionMethod.App:
|
||||||
return {
|
return {
|
||||||
// access token not needed for GitHub App
|
|
||||||
installationId: credentials.installationId
|
installationId: credentials.installationId
|
||||||
};
|
};
|
||||||
case GitHubConnectionMethod.OAuth:
|
case GitHubConnectionMethod.OAuth:
|
||||||
|
@ -57,7 +57,7 @@ export const UpdateGitHubConnectionSchema = z
|
|||||||
|
|
||||||
const BaseGitHubConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.GitHub) });
|
const BaseGitHubConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.GitHub) });
|
||||||
|
|
||||||
export const GitHubAppConnectionSchema = z.intersection(
|
export const GitHubConnectionSchema = z.intersection(
|
||||||
BaseGitHubConnectionSchema,
|
BaseGitHubConnectionSchema,
|
||||||
z.discriminatedUnion("method", [
|
z.discriminatedUnion("method", [
|
||||||
z.object({
|
z.object({
|
||||||
@ -74,19 +74,19 @@ export const GitHubAppConnectionSchema = z.intersection(
|
|||||||
export const SanitizedGitHubConnectionSchema = z.discriminatedUnion("method", [
|
export const SanitizedGitHubConnectionSchema = z.discriminatedUnion("method", [
|
||||||
BaseGitHubConnectionSchema.extend({
|
BaseGitHubConnectionSchema.extend({
|
||||||
method: z.literal(GitHubConnectionMethod.App),
|
method: z.literal(GitHubConnectionMethod.App),
|
||||||
credentials: GitHubConnectionAppOutputCredentialsSchema.omit({ installationId: true })
|
credentials: GitHubConnectionAppOutputCredentialsSchema.pick({})
|
||||||
}),
|
}),
|
||||||
BaseGitHubConnectionSchema.extend({
|
BaseGitHubConnectionSchema.extend({
|
||||||
method: z.literal(GitHubConnectionMethod.OAuth),
|
method: z.literal(GitHubConnectionMethod.OAuth),
|
||||||
credentials: GitHubConnectionOAuthOutputCredentialsSchema.omit({ accessToken: true })
|
credentials: GitHubConnectionOAuthOutputCredentialsSchema.pick({})
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const GitHubConnectionListItemSchema = z.object({
|
export const GitHubConnectionListItemSchema = z.object({
|
||||||
name: z.literal("GitHub"),
|
name: z.literal("GitHub"),
|
||||||
app: z.literal(AppConnection.GitHub),
|
app: z.literal(AppConnection.GitHub),
|
||||||
// the below is preferable but currently breaks mintlify
|
// the below is preferable but currently breaks with our zod to json schema parser
|
||||||
// methods: z.tuple([z.literal(GitHubConnectionMethod.GitHubApp), z.literal(GitHubConnectionMethod.OAuth)]),
|
// methods: z.tuple([z.literal(GitHubConnectionMethod.App), z.literal(GitHubConnectionMethod.OAuth)]),
|
||||||
methods: z.nativeEnum(GitHubConnectionMethod).array(),
|
methods: z.nativeEnum(GitHubConnectionMethod).array(),
|
||||||
oauthClientId: z.string().optional(),
|
oauthClientId: z.string().optional(),
|
||||||
appClientSlug: z.string().optional()
|
appClientSlug: z.string().optional()
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
|
import {
|
||||||
|
getGitHubEnvironments,
|
||||||
|
getGitHubOrganizations,
|
||||||
|
getGitHubRepositories
|
||||||
|
} from "@app/services/app-connection/github/github-connection-fns";
|
||||||
|
import { TGitHubConnection } from "@app/services/app-connection/github/github-connection-types";
|
||||||
|
|
||||||
|
type TGetAppConnectionFunc = (
|
||||||
|
app: AppConnection,
|
||||||
|
connectionId: string,
|
||||||
|
actor: OrgServiceActor
|
||||||
|
) => Promise<TGitHubConnection>;
|
||||||
|
|
||||||
|
type TListGitHubEnvironmentsDTO = {
|
||||||
|
connectionId: string;
|
||||||
|
repo: string;
|
||||||
|
owner: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const githubConnectionService = (getAppConnection: TGetAppConnectionFunc) => {
|
||||||
|
const listRepositories = async (connectionId: string, actor: OrgServiceActor) => {
|
||||||
|
const appConnection = await getAppConnection(AppConnection.GitHub, connectionId, actor);
|
||||||
|
|
||||||
|
const repositories = await getGitHubRepositories(appConnection);
|
||||||
|
|
||||||
|
return repositories;
|
||||||
|
};
|
||||||
|
|
||||||
|
const listOrganizations = async (connectionId: string, actor: OrgServiceActor) => {
|
||||||
|
const appConnection = await getAppConnection(AppConnection.GitHub, connectionId, actor);
|
||||||
|
|
||||||
|
const organizations = await getGitHubOrganizations(appConnection);
|
||||||
|
|
||||||
|
return organizations;
|
||||||
|
};
|
||||||
|
|
||||||
|
const listEnvironments = async (
|
||||||
|
{ connectionId, repo, owner }: TListGitHubEnvironmentsDTO,
|
||||||
|
actor: OrgServiceActor
|
||||||
|
) => {
|
||||||
|
const appConnection = await getAppConnection(AppConnection.GitHub, connectionId, actor);
|
||||||
|
|
||||||
|
const environments = await getGitHubEnvironments(appConnection, owner, repo);
|
||||||
|
|
||||||
|
return environments;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
listRepositories,
|
||||||
|
listOrganizations,
|
||||||
|
listEnvironments
|
||||||
|
};
|
||||||
|
};
|
@ -5,11 +5,11 @@ import { DiscriminativePick } from "@app/lib/types";
|
|||||||
import { AppConnection } from "../app-connection-enums";
|
import { AppConnection } from "../app-connection-enums";
|
||||||
import {
|
import {
|
||||||
CreateGitHubConnectionSchema,
|
CreateGitHubConnectionSchema,
|
||||||
GitHubAppConnectionSchema,
|
GitHubConnectionSchema,
|
||||||
ValidateGitHubConnectionCredentialsSchema
|
ValidateGitHubConnectionCredentialsSchema
|
||||||
} from "./github-connection-schemas";
|
} from "./github-connection-schemas";
|
||||||
|
|
||||||
export type TGitHubConnection = z.infer<typeof GitHubAppConnectionSchema>;
|
export type TGitHubConnection = z.infer<typeof GitHubConnectionSchema>;
|
||||||
|
|
||||||
export type TGitHubConnectionInput = z.infer<typeof CreateGitHubConnectionSchema> & {
|
export type TGitHubConnectionInput = z.infer<typeof CreateGitHubConnectionSchema> & {
|
||||||
app: AppConnection.GitHub;
|
app: AppConnection.GitHub;
|
||||||
|
@ -39,7 +39,8 @@ export enum ActorType { // would extend to AWS, Azure, ...
|
|||||||
SERVICE = "service",
|
SERVICE = "service",
|
||||||
IDENTITY = "identity",
|
IDENTITY = "identity",
|
||||||
Machine = "machine",
|
Machine = "machine",
|
||||||
SCIM_CLIENT = "scimClient"
|
SCIM_CLIENT = "scimClient",
|
||||||
|
UNKNOWN_USER = "unknownUser"
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be null unless the token-type is JWT
|
// This will be null unless the token-type is JWT
|
||||||
|
@ -5,7 +5,7 @@ import crypto, { KeyObject } from "crypto";
|
|||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ProjectType, TCertificateAuthorities, TCertificateTemplates } from "@app/db/schemas";
|
import { ActionProjectType, ProjectType, TCertificateAuthorities, TCertificateTemplates } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
@ -136,14 +136,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
projectId = certManagerProjectFromSplit.id;
|
projectId = certManagerProjectFromSplit.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
@ -305,13 +305,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
ProjectPermissionSub.CertificateAuthorities
|
ProjectPermissionSub.CertificateAuthorities
|
||||||
@ -336,14 +337,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
@ -362,14 +363,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Delete,
|
ProjectPermissionActions.Delete,
|
||||||
@ -388,13 +389,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
@ -449,14 +451,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
|
|
||||||
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
@ -720,13 +722,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
@ -755,13 +758,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
||||||
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
@ -835,14 +839,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new NotFoundError({ message: "CA not found" });
|
if (!ca) throw new NotFoundError({ message: "CA not found" });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
@ -982,14 +986,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
@ -1145,14 +1149,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Certificates);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Certificates);
|
||||||
|
|
||||||
@ -1474,14 +1478,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!dto.isInternal) {
|
if (!dto.isInternal) {
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
dto.actor,
|
actor: dto.actor,
|
||||||
dto.actorId,
|
actorId: dto.actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
dto.actorAuthMethod,
|
actorAuthMethod: dto.actorAuthMethod,
|
||||||
dto.actorOrgId
|
actorOrgId: dto.actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
@ -1832,13 +1836,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
if (!ca) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
|
@ -2,7 +2,7 @@ import { ForbiddenError } from "@casl/ability";
|
|||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
|
|
||||||
import { ProjectType, TCertificateTemplateEstConfigsUpdate } from "@app/db/schemas";
|
import { ActionProjectType, TCertificateTemplateEstConfigsUpdate } 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";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
@ -67,14 +67,14 @@ export const certificateTemplateServiceFactory = ({
|
|||||||
message: `CA with ID ${caId} not found`
|
message: `CA with ID ${caId} not found`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
@ -129,14 +129,14 @@ export const certificateTemplateServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
certTemplate.projectId,
|
projectId: certTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
@ -187,14 +187,14 @@ export const certificateTemplateServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
certTemplate.projectId,
|
projectId: certTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Delete,
|
ProjectPermissionActions.Delete,
|
||||||
@ -214,13 +214,14 @@ export const certificateTemplateServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
certTemplate.projectId,
|
projectId: certTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
@ -255,14 +256,14 @@ export const certificateTemplateServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
certTemplate.projectId,
|
projectId: certTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
@ -340,14 +341,14 @@ export const certificateTemplateServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
certTemplate.projectId,
|
projectId: certTemplate.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
@ -422,13 +423,14 @@ export const certificateTemplateServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!dto.isInternal) {
|
if (!dto.isInternal) {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
dto.actor,
|
actor: dto.actor,
|
||||||
dto.actorId,
|
actorId: dto.actorId,
|
||||||
certTemplate.projectId,
|
projectId: certTemplate.projectId,
|
||||||
dto.actorAuthMethod,
|
actorAuthMethod: dto.actorAuthMethod,
|
||||||
dto.actorOrgId
|
actorOrgId: dto.actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
|
@ -1,7 +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 { ProjectType } from "@app/db/schemas";
|
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";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
@ -50,14 +50,14 @@ export const certificateServiceFactory = ({
|
|||||||
const cert = await certificateDAL.findOne({ serialNumber });
|
const cert = await certificateDAL.findOne({ serialNumber });
|
||||||
const ca = await certificateAuthorityDAL.findById(cert.caId);
|
const ca = await certificateAuthorityDAL.findById(cert.caId);
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates);
|
||||||
|
|
||||||
@ -74,14 +74,14 @@ export const certificateServiceFactory = ({
|
|||||||
const cert = await certificateDAL.findOne({ serialNumber });
|
const cert = await certificateDAL.findOne({ serialNumber });
|
||||||
const ca = await certificateAuthorityDAL.findById(cert.caId);
|
const ca = await certificateAuthorityDAL.findById(cert.caId);
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Certificates);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Certificates);
|
||||||
|
|
||||||
@ -109,14 +109,14 @@ export const certificateServiceFactory = ({
|
|||||||
const cert = await certificateDAL.findOne({ serialNumber });
|
const cert = await certificateDAL.findOne({ serialNumber });
|
||||||
const ca = await certificateAuthorityDAL.findById(cert.caId);
|
const ca = await certificateAuthorityDAL.findById(cert.caId);
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.CertificateManager);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Certificates);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Certificates);
|
||||||
|
|
||||||
@ -156,13 +156,14 @@ export const certificateServiceFactory = ({
|
|||||||
const cert = await certificateDAL.findOne({ serialNumber });
|
const cert = await certificateDAL.findOne({ serialNumber });
|
||||||
const ca = await certificateAuthorityDAL.findById(cert.caId);
|
const ca = await certificateAuthorityDAL.findById(cert.caId);
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
ca.projectId,
|
projectId: ca.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
import { ProjectType } from "@app/db/schemas";
|
import { ActionProjectType, ProjectType } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { ProjectPermissionCmekActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionCmekActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
@ -34,14 +34,14 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
|
|||||||
projectId = cmekProjectFromSplit.id;
|
projectId = cmekProjectFromSplit.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
projectId,
|
projectId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
actionProjectType: ActionProjectType.KMS
|
||||||
ForbidOnInvalidProjectType(ProjectType.KMS);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Create, ProjectPermissionSub.Cmek);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Create, ProjectPermissionSub.Cmek);
|
||||||
|
|
||||||
const cmek = await kmsService.generateKmsKey({
|
const cmek = await kmsService.generateKmsKey({
|
||||||
@ -60,14 +60,14 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
|
|||||||
|
|
||||||
if (!key.projectId || key.isReserved) throw new BadRequestError({ message: "Key is not customer managed" });
|
if (!key.projectId || key.isReserved) throw new BadRequestError({ message: "Key is not customer managed" });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
key.projectId,
|
projectId: key.projectId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
actionProjectType: ActionProjectType.KMS
|
||||||
ForbidOnInvalidProjectType(ProjectType.KMS);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Edit, ProjectPermissionSub.Cmek);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Edit, ProjectPermissionSub.Cmek);
|
||||||
|
|
||||||
@ -83,14 +83,14 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
|
|||||||
|
|
||||||
if (!key.projectId || key.isReserved) throw new BadRequestError({ message: "Key is not customer managed" });
|
if (!key.projectId || key.isReserved) throw new BadRequestError({ message: "Key is not customer managed" });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
key.projectId,
|
projectId: key.projectId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
actionProjectType: ActionProjectType.KMS
|
||||||
ForbidOnInvalidProjectType(ProjectType.KMS);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Delete, ProjectPermissionSub.Cmek);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Delete, ProjectPermissionSub.Cmek);
|
||||||
|
|
||||||
@ -109,13 +109,14 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
|
|||||||
projectId = cmekProjectFromSplit.id;
|
projectId = cmekProjectFromSplit.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
projectId,
|
projectId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
actionProjectType: ActionProjectType.KMS
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
|
||||||
|
|
||||||
@ -133,15 +134,15 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
|
|||||||
|
|
||||||
if (key.isDisabled) throw new BadRequestError({ message: "Key is disabled" });
|
if (key.isDisabled) throw new BadRequestError({ message: "Key is disabled" });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
key.projectId,
|
projectId: key.projectId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
actionProjectType: ActionProjectType.KMS
|
||||||
|
});
|
||||||
|
|
||||||
ForbidOnInvalidProjectType(ProjectType.KMS);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Encrypt, ProjectPermissionSub.Cmek);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Encrypt, ProjectPermissionSub.Cmek);
|
||||||
|
|
||||||
const encrypt = await kmsService.encryptWithKmsKey({ kmsId: keyId });
|
const encrypt = await kmsService.encryptWithKmsKey({ kmsId: keyId });
|
||||||
@ -160,14 +161,14 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
|
|||||||
|
|
||||||
if (key.isDisabled) throw new BadRequestError({ message: "Key is disabled" });
|
if (key.isDisabled) throw new BadRequestError({ message: "Key is disabled" });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor.type,
|
actor: actor.type,
|
||||||
actor.id,
|
actorId: actor.id,
|
||||||
key.projectId,
|
projectId: key.projectId,
|
||||||
actor.authMethod,
|
actorAuthMethod: actor.authMethod,
|
||||||
actor.orgId
|
actorOrgId: actor.orgId,
|
||||||
);
|
actionProjectType: ActionProjectType.KMS
|
||||||
ForbidOnInvalidProjectType(ProjectType.KMS);
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Decrypt, ProjectPermissionSub.Cmek);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Decrypt, ProjectPermissionSub.Cmek);
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
|
|
||||||
import { ProjectMembershipRole, SecretKeyEncoding } from "@app/db/schemas";
|
import { ActionProjectType, ProjectMembershipRole, SecretKeyEncoding, TGroups } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||||
@ -9,6 +9,7 @@ import { decryptAsymmetric, encryptAsymmetric } from "@app/lib/crypto";
|
|||||||
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
||||||
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";
|
||||||
|
import { isUuidV4 } from "@app/lib/validator";
|
||||||
|
|
||||||
import { TGroupDALFactory } from "../../ee/services/group/group-dal";
|
import { TGroupDALFactory } from "../../ee/services/group/group-dal";
|
||||||
import { TUserGroupMembershipDALFactory } from "../../ee/services/group/user-group-membership-dal";
|
import { TUserGroupMembershipDALFactory } from "../../ee/services/group/user-group-membership-dal";
|
||||||
@ -62,29 +63,37 @@ export const groupProjectServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
roles,
|
roles,
|
||||||
projectId,
|
projectId,
|
||||||
groupId
|
groupIdOrName
|
||||||
}: TCreateProjectGroupDTO) => {
|
}: TCreateProjectGroupDTO) => {
|
||||||
const project = await projectDAL.findById(projectId);
|
const project = await projectDAL.findById(projectId);
|
||||||
|
|
||||||
if (!project) throw new NotFoundError({ message: `Failed to find project with ID ${projectId}` });
|
if (!project) throw new NotFoundError({ message: `Failed to find project with ID ${projectId}` });
|
||||||
if (project.version < 2) throw new BadRequestError({ message: `Failed to add group to E2EE project` });
|
if (project.version < 2) throw new BadRequestError({ message: `Failed to add group to E2EE project` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
project.id,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Groups);
|
||||||
|
|
||||||
const group = await groupDAL.findOne({ orgId: actorOrgId, id: groupId });
|
let group: TGroups | null = null;
|
||||||
if (!group) throw new NotFoundError({ message: `Failed to find group with ID ${groupId}` });
|
if (isUuidV4(groupIdOrName)) {
|
||||||
|
group = await groupDAL.findOne({ orgId: actorOrgId, id: groupIdOrName });
|
||||||
|
}
|
||||||
|
if (!group) {
|
||||||
|
group = await groupDAL.findOne({ orgId: actorOrgId, name: groupIdOrName });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!group) throw new NotFoundError({ message: `Failed to find group with ID or name ${groupIdOrName}` });
|
||||||
|
|
||||||
const existingGroup = await groupProjectDAL.findOne({ groupId: group.id, projectId: project.id });
|
const existingGroup = await groupProjectDAL.findOne({ groupId: group.id, projectId: project.id });
|
||||||
if (existingGroup)
|
if (existingGroup)
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: `Group with ID ${groupId} already exists in project with id ${project.id}`
|
message: `Group with ID ${group.id} already exists in project with id ${project.id}`
|
||||||
});
|
});
|
||||||
|
|
||||||
for await (const { role: requestedRoleChange } of roles) {
|
for await (const { role: requestedRoleChange } of roles) {
|
||||||
@ -127,7 +136,7 @@ export const groupProjectServiceFactory = ({
|
|||||||
const projectGroup = await groupProjectDAL.transaction(async (tx) => {
|
const projectGroup = await groupProjectDAL.transaction(async (tx) => {
|
||||||
const groupProjectMembership = await groupProjectDAL.create(
|
const groupProjectMembership = await groupProjectDAL.create(
|
||||||
{
|
{
|
||||||
groupId: group.id,
|
groupId: group!.id,
|
||||||
projectId: project.id
|
projectId: project.id
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
@ -162,7 +171,7 @@ export const groupProjectServiceFactory = ({
|
|||||||
// share project key with users in group that have not
|
// share project key with users in group that have not
|
||||||
// individually been added to the project and that are not part of
|
// individually been added to the project and that are not part of
|
||||||
// other groups that are in the project
|
// other groups that are in the project
|
||||||
const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group.id, project.id, tx);
|
const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group!.id, project.id, tx);
|
||||||
|
|
||||||
if (groupMembers.length) {
|
if (groupMembers.length) {
|
||||||
const ghostUser = await projectDAL.findProjectGhostUser(project.id, tx);
|
const ghostUser = await projectDAL.findProjectGhostUser(project.id, tx);
|
||||||
@ -237,13 +246,14 @@ export const groupProjectServiceFactory = ({
|
|||||||
|
|
||||||
if (!project) throw new NotFoundError({ message: `Failed to find project with ID ${projectId}` });
|
if (!project) throw new NotFoundError({ message: `Failed to find project with ID ${projectId}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
project.id,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Groups);
|
||||||
|
|
||||||
const group = await groupDAL.findOne({ orgId: actorOrgId, id: groupId });
|
const group = await groupDAL.findOne({ orgId: actorOrgId, id: groupId });
|
||||||
@ -339,13 +349,14 @@ export const groupProjectServiceFactory = ({
|
|||||||
const groupProjectMembership = await groupProjectDAL.findOne({ groupId: group.id, projectId: project.id });
|
const groupProjectMembership = await groupProjectDAL.findOne({ groupId: group.id, projectId: project.id });
|
||||||
if (!groupProjectMembership) throw new NotFoundError({ message: `Failed to find group with ID ${groupId}` });
|
if (!groupProjectMembership) throw new NotFoundError({ message: `Failed to find group with ID ${groupId}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
project.id,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Groups);
|
||||||
|
|
||||||
const deletedProjectGroup = await groupProjectDAL.transaction(async (tx) => {
|
const deletedProjectGroup = await groupProjectDAL.transaction(async (tx) => {
|
||||||
@ -383,13 +394,14 @@ export const groupProjectServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: `Failed to find project with ID ${projectId}` });
|
throw new NotFoundError({ message: `Failed to find project with ID ${projectId}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
project.id,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Groups);
|
||||||
|
|
||||||
const groupMemberships = await groupProjectDAL.findByProjectId(project.id);
|
const groupMemberships = await groupProjectDAL.findByProjectId(project.id);
|
||||||
@ -410,13 +422,14 @@ export const groupProjectServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: `Failed to find project with ID ${projectId}` });
|
throw new NotFoundError({ message: `Failed to find project with ID ${projectId}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
project.id,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Groups);
|
||||||
|
|
||||||
const [groupMembership] = await groupProjectDAL.findByProjectId(project.id, {
|
const [groupMembership] = await groupProjectDAL.findByProjectId(project.id, {
|
||||||
|
@ -3,7 +3,7 @@ import { TProjectPermission } from "@app/lib/types";
|
|||||||
import { ProjectUserMembershipTemporaryMode } from "../project-membership/project-membership-types";
|
import { ProjectUserMembershipTemporaryMode } from "../project-membership/project-membership-types";
|
||||||
|
|
||||||
export type TCreateProjectGroupDTO = {
|
export type TCreateProjectGroupDTO = {
|
||||||
groupId: string;
|
groupIdOrName: string;
|
||||||
roles: (
|
roles: (
|
||||||
| {
|
| {
|
||||||
role: string;
|
role: string;
|
||||||
|
@ -2,3 +2,11 @@ import picomatch from "picomatch";
|
|||||||
|
|
||||||
export const doesFieldValueMatchOidcPolicy = (fieldValue: string, policyValue: string) =>
|
export const doesFieldValueMatchOidcPolicy = (fieldValue: string, policyValue: string) =>
|
||||||
policyValue === fieldValue || picomatch.isMatch(fieldValue, policyValue);
|
policyValue === fieldValue || picomatch.isMatch(fieldValue, policyValue);
|
||||||
|
|
||||||
|
export const doesAudValueMatchOidcPolicy = (fieldValue: string | string[], policyValue: string) => {
|
||||||
|
if (Array.isArray(fieldValue)) {
|
||||||
|
return fieldValue.some((entry) => entry === policyValue || picomatch.isMatch(entry, policyValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
return policyValue === fieldValue || picomatch.isMatch(fieldValue, policyValue);
|
||||||
|
};
|
||||||
|
@ -27,7 +27,7 @@ import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identit
|
|||||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||||
import { TOrgBotDALFactory } from "../org/org-bot-dal";
|
import { TOrgBotDALFactory } from "../org/org-bot-dal";
|
||||||
import { TIdentityOidcAuthDALFactory } from "./identity-oidc-auth-dal";
|
import { TIdentityOidcAuthDALFactory } from "./identity-oidc-auth-dal";
|
||||||
import { doesFieldValueMatchOidcPolicy } from "./identity-oidc-auth-fns";
|
import { doesAudValueMatchOidcPolicy, doesFieldValueMatchOidcPolicy } from "./identity-oidc-auth-fns";
|
||||||
import {
|
import {
|
||||||
TAttachOidcAuthDTO,
|
TAttachOidcAuthDTO,
|
||||||
TGetOidcAuthDTO,
|
TGetOidcAuthDTO,
|
||||||
@ -148,7 +148,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
if (
|
if (
|
||||||
!identityOidcAuth.boundAudiences
|
!identityOidcAuth.boundAudiences
|
||||||
.split(", ")
|
.split(", ")
|
||||||
.some((policyValue) => doesFieldValueMatchOidcPolicy(tokenData.aud, policyValue))
|
.some((policyValue) => doesAudValueMatchOidcPolicy(tokenData.aud, policyValue))
|
||||||
) {
|
) {
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
message: "Access denied: OIDC audience not allowed."
|
message: "Access denied: OIDC audience not allowed."
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
|
|
||||||
import { ProjectMembershipRole } from "@app/db/schemas";
|
import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||||
@ -54,13 +54,14 @@ export const identityProjectServiceFactory = ({
|
|||||||
projectId,
|
projectId,
|
||||||
roles
|
roles
|
||||||
}: TCreateProjectIdentityDTO) => {
|
}: TCreateProjectIdentityDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
subject(ProjectPermissionSub.Identity, {
|
subject(ProjectPermissionSub.Identity, {
|
||||||
@ -159,13 +160,14 @@ export const identityProjectServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TUpdateProjectIdentityDTO) => {
|
}: TUpdateProjectIdentityDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
@ -254,25 +256,27 @@ export const identityProjectServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
identityProjectMembership.projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Delete,
|
ProjectPermissionActions.Delete,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
);
|
);
|
||||||
|
|
||||||
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
|
const { permission: identityRolePermission } = await permissionService.getProjectPermission({
|
||||||
ActorType.IDENTITY,
|
actor: ActorType.IDENTITY,
|
||||||
identityId,
|
actorId: identityId,
|
||||||
identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
if (!isAtLeastAsPrivileged(permission, identityRolePermission))
|
if (!isAtLeastAsPrivileged(permission, identityRolePermission))
|
||||||
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
|
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
|
||||||
|
|
||||||
@ -292,13 +296,14 @@ export const identityProjectServiceFactory = ({
|
|||||||
orderDirection,
|
orderDirection,
|
||||||
search
|
search
|
||||||
}: TListProjectIdentityDTO) => {
|
}: TListProjectIdentityDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Identity);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Identity);
|
||||||
|
|
||||||
const identityMemberships = await identityProjectDAL.findByProjectId(projectId, {
|
const identityMemberships = await identityProjectDAL.findByProjectId(projectId, {
|
||||||
@ -322,13 +327,14 @@ export const identityProjectServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
identityId
|
identityId
|
||||||
}: TGetProjectIdentityByIdentityIdDTO) => {
|
}: TGetProjectIdentityByIdentityIdDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionActions.Read,
|
||||||
|
@ -5,7 +5,7 @@ import { Client as OctopusClient, SpaceRepository as OctopusSpaceRepository } fr
|
|||||||
import AWS from "aws-sdk";
|
import AWS from "aws-sdk";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ProjectType,
|
ActionProjectType,
|
||||||
SecretEncryptionAlgo,
|
SecretEncryptionAlgo,
|
||||||
SecretKeyEncoding,
|
SecretKeyEncoding,
|
||||||
TIntegrationAuths,
|
TIntegrationAuths,
|
||||||
@ -97,13 +97,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
projectId
|
projectId
|
||||||
}: TProjectPermission) => {
|
}: TProjectPermission) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const authorizations = await integrationAuthDAL.find({ projectId });
|
const authorizations = await integrationAuthDAL.find({ projectId });
|
||||||
return authorizations;
|
return authorizations;
|
||||||
@ -114,13 +115,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
authorizations.filter(async (auth) => {
|
authorizations.filter(async (auth) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
auth.projectId,
|
projectId: auth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
|
|
||||||
return permission.can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
return permission.can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
})
|
})
|
||||||
@ -131,13 +133,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
return integrationAuth;
|
return integrationAuth;
|
||||||
};
|
};
|
||||||
@ -156,14 +159,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
if (!Object.values(Integrations).includes(integration as Integrations))
|
if (!Object.values(Integrations).includes(integration as Integrations))
|
||||||
throw new BadRequestError({ message: "Invalid integration" });
|
throw new BadRequestError({ message: "Invalid integration" });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
const tokenExchange = await exchangeCode({ integration, code, url, installationId });
|
const tokenExchange = await exchangeCode({ integration, code, url, installationId });
|
||||||
@ -266,14 +269,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
if (!Object.values(Integrations).includes(integration as Integrations))
|
if (!Object.values(Integrations).includes(integration as Integrations))
|
||||||
throw new BadRequestError({ message: "Invalid integration" });
|
throw new BadRequestError({ message: "Invalid integration" });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
const updateDoc: TIntegrationAuthsInsert = {
|
const updateDoc: TIntegrationAuthsInsert = {
|
||||||
@ -401,13 +404,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: `Integration auth with id ${integrationAuthId} not found.` });
|
throw new NotFoundError({ message: `Integration auth with id ${integrationAuthId} not found.` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
const { projectId } = integrationAuth;
|
const { projectId } = integrationAuth;
|
||||||
@ -662,13 +666,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { botKey, shouldUseSecretV2Bridge } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
@ -696,13 +701,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
@ -726,13 +732,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -767,13 +774,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -795,13 +803,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
|
|
||||||
@ -869,13 +878,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -916,13 +926,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -950,13 +961,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessId, accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessId, accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1008,13 +1020,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1044,13 +1057,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1085,13 +1099,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1125,13 +1140,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1165,13 +1181,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID ${id} not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID ${id} not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1204,13 +1221,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1244,13 +1262,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1312,13 +1331,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1386,13 +1406,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1436,13 +1457,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1484,13 +1506,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1552,13 +1575,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1593,13 +1617,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1705,13 +1730,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TDeleteIntegrationAuthsDTO) => {
|
}: TDeleteIntegrationAuthsDTO) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
const integrations = await integrationAuthDAL.delete({ integration, projectId });
|
const integrations = await integrationAuthDAL.delete({ integration, projectId });
|
||||||
@ -1728,13 +1754,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
const delIntegrationAuth = await integrationAuthDAL.transaction(async (tx) => {
|
const delIntegrationAuth = await integrationAuthDAL.transaction(async (tx) => {
|
||||||
@ -1761,26 +1788,28 @@ export const integrationAuthServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission: sourcePermission } = await permissionService.getProjectPermission(
|
const { permission: sourcePermission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(sourcePermission).throwUnlessCan(
|
ForbiddenError.from(sourcePermission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
ProjectPermissionSub.Integrations
|
ProjectPermissionSub.Integrations
|
||||||
);
|
);
|
||||||
|
|
||||||
const { permission: targetPermission } = await permissionService.getProjectPermission(
|
const { permission: targetPermission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(targetPermission).throwUnlessCan(
|
ForbiddenError.from(targetPermission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
@ -1806,13 +1835,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
@ -1846,13 +1876,14 @@ export const integrationAuthServiceFactory = ({
|
|||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
const { accessToken } = await getIntegrationAccessToken(integrationAuth, shouldUseSecretV2Bridge, botKey);
|
||||||
|
@ -932,15 +932,22 @@ const syncSecretsAWSParameterStore = async ({
|
|||||||
logger.info(
|
logger.info(
|
||||||
`getIntegrationSecrets: create secret in AWS SSM for [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}]`
|
`getIntegrationSecrets: create secret in AWS SSM for [projectId=${projectId}] [environment=${integration.environment.slug}] [secretPath=${integration.secretPath}]`
|
||||||
);
|
);
|
||||||
await ssm
|
|
||||||
.putParameter({
|
try {
|
||||||
Name: `${integration.path}${key}`,
|
await ssm
|
||||||
Type: "SecureString",
|
.putParameter({
|
||||||
Value: secrets[key].value,
|
Name: `${integration.path}${key}`,
|
||||||
...(metadata.kmsKeyId && { KeyId: metadata.kmsKeyId }),
|
Type: "SecureString",
|
||||||
Overwrite: true
|
Value: secrets[key].value,
|
||||||
})
|
...(metadata.kmsKeyId && { KeyId: metadata.kmsKeyId }),
|
||||||
.promise();
|
Overwrite: true
|
||||||
|
})
|
||||||
|
.promise();
|
||||||
|
} catch (error) {
|
||||||
|
(error as { secretKey: string }).secretKey = key;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
if (metadata.secretAWSTag?.length) {
|
if (metadata.secretAWSTag?.length) {
|
||||||
try {
|
try {
|
||||||
await ssm
|
await ssm
|
||||||
@ -987,15 +994,20 @@ const syncSecretsAWSParameterStore = async ({
|
|||||||
|
|
||||||
// we ensure that the KMS key configured in the integration is applied for ALL parameters on AWS
|
// we ensure that the KMS key configured in the integration is applied for ALL parameters on AWS
|
||||||
if (secrets[key].value && (shouldUpdateKms || awsParameterStoreSecretsObj[key].Value !== secrets[key].value)) {
|
if (secrets[key].value && (shouldUpdateKms || awsParameterStoreSecretsObj[key].Value !== secrets[key].value)) {
|
||||||
await ssm
|
try {
|
||||||
.putParameter({
|
await ssm
|
||||||
Name: `${integration.path}${key}`,
|
.putParameter({
|
||||||
Type: "SecureString",
|
Name: `${integration.path}${key}`,
|
||||||
Value: secrets[key].value,
|
Type: "SecureString",
|
||||||
Overwrite: true,
|
Value: secrets[key].value,
|
||||||
...(metadata.kmsKeyId && { KeyId: metadata.kmsKeyId })
|
Overwrite: true,
|
||||||
})
|
...(metadata.kmsKeyId && { KeyId: metadata.kmsKeyId })
|
||||||
.promise();
|
})
|
||||||
|
.promise();
|
||||||
|
} catch (error) {
|
||||||
|
(error as { secretKey: string }).secretKey = key;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (awsParameterStoreSecretsObj[key].Name) {
|
if (awsParameterStoreSecretsObj[key].Name) {
|
||||||
@ -1289,7 +1301,10 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
|
|
||||||
if (metadata.mappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE) {
|
if (metadata.mappingBehavior === IntegrationMappingBehavior.ONE_TO_ONE) {
|
||||||
for await (const [key, value] of Object.entries(secrets)) {
|
for await (const [key, value] of Object.entries(secrets)) {
|
||||||
await processAwsSecret(key, value.value, value.secretMetadata);
|
await processAwsSecret(key, value.value, value.secretMetadata).catch((error) => {
|
||||||
|
error.secretKey = key;
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await processAwsSecret(integration.app as string, getSecretKeyValuePair(secrets));
|
await processAwsSecret(integration.app as string, getSecretKeyValuePair(secrets));
|
||||||
@ -3747,17 +3762,28 @@ const syncSecretsCloudflarePages = async ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const metadata = z.record(z.any()).parse(integration.metadata);
|
const metadata = z.record(z.any()).parse(integration.metadata);
|
||||||
if (metadata.shouldAutoRedeploy) {
|
if (metadata.shouldAutoRedeploy && integration.targetEnvironment === "production") {
|
||||||
await request.post(
|
await request
|
||||||
`${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}/deployments`,
|
.post(
|
||||||
{},
|
`${IntegrationUrls.CLOUDFLARE_PAGES_API_URL}/client/v4/accounts/${accessId}/pages/projects/${integration.app}/deployments`,
|
||||||
{
|
{},
|
||||||
headers: {
|
{
|
||||||
Authorization: `Bearer ${accessToken}`,
|
headers: {
|
||||||
Accept: "application/json"
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
Accept: "application/json"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
);
|
.catch((error) => {
|
||||||
|
if (error instanceof AxiosError && error.response?.status === 304) {
|
||||||
|
logger.info(
|
||||||
|
`syncSecretsCloudflarePages: CF pages redeployment returned status code 304 for integration [id=${integration.id}]`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
|
|
||||||
import { ProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
import { NotFoundError } from "@app/lib/errors";
|
import { NotFoundError } from "@app/lib/errors";
|
||||||
@ -81,14 +81,14 @@ export const integrationServiceFactory = ({
|
|||||||
if (!integrationAuth)
|
if (!integrationAuth)
|
||||||
throw new NotFoundError({ message: `Integration auth with ID '${integrationAuthId}' not found` });
|
throw new NotFoundError({ message: `Integration auth with ID '${integrationAuthId}' not found` });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integrationAuth.projectId,
|
projectId: integrationAuth.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
@ -160,14 +160,14 @@ export const integrationServiceFactory = ({
|
|||||||
const integration = await integrationDAL.findById(id);
|
const integration = await integrationDAL.findById(id);
|
||||||
if (!integration) throw new NotFoundError({ message: `Integration with ID '${id}' not found` });
|
if (!integration) throw new NotFoundError({ message: `Integration with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integration.projectId,
|
projectId: integration.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
const newEnvironment = environment || integration.environment.slug;
|
const newEnvironment = environment || integration.environment.slug;
|
||||||
@ -227,13 +227,14 @@ export const integrationServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integration?.projectId || "",
|
projectId: integration.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
if (!integration) {
|
if (!integration) {
|
||||||
@ -254,13 +255,14 @@ export const integrationServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integration?.projectId || "",
|
projectId: integration.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
const integrationAuth = await integrationAuthDAL.findById(integration.integrationAuthId);
|
const integrationAuth = await integrationAuthDAL.findById(integration.integrationAuthId);
|
||||||
@ -296,14 +298,14 @@ export const integrationServiceFactory = ({
|
|||||||
const integration = await integrationDAL.findById(id);
|
const integration = await integrationDAL.findById(id);
|
||||||
if (!integration) throw new NotFoundError({ message: `Integration with ID '${id}' not found` });
|
if (!integration) throw new NotFoundError({ message: `Integration with ID '${id}' not found` });
|
||||||
|
|
||||||
const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integration.projectId,
|
projectId: integration.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
ForbidOnInvalidProjectType(ProjectType.SecretManager);
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
const integrationAuth = await integrationAuthDAL.findById(integration.integrationAuthId);
|
const integrationAuth = await integrationAuthDAL.findById(integration.integrationAuthId);
|
||||||
@ -333,13 +335,14 @@ export const integrationServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
projectId
|
projectId
|
||||||
}: TProjectPermission) => {
|
}: TProjectPermission) => {
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
const integrations = await integrationDAL.findByProjectId(projectId);
|
const integrations = await integrationDAL.findByProjectId(projectId);
|
||||||
@ -352,13 +355,14 @@ export const integrationServiceFactory = ({
|
|||||||
throw new NotFoundError({ message: `Integration with ID '${id}' not found` });
|
throw new NotFoundError({ message: `Integration with ID '${id}' not found` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
integration.projectId,
|
projectId: integration.projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
||||||
|
|
||||||
await secretQueueService.syncIntegrations({
|
await secretQueueService.syncIntegrations({
|
||||||
|
@ -5,6 +5,7 @@ import jwt from "jsonwebtoken";
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ActionProjectType,
|
||||||
OrgMembershipRole,
|
OrgMembershipRole,
|
||||||
OrgMembershipStatus,
|
OrgMembershipStatus,
|
||||||
ProjectMembershipRole,
|
ProjectMembershipRole,
|
||||||
@ -68,6 +69,7 @@ import {
|
|||||||
TGetOrgMembershipDTO,
|
TGetOrgMembershipDTO,
|
||||||
TInviteUserToOrgDTO,
|
TInviteUserToOrgDTO,
|
||||||
TListProjectMembershipsByOrgMembershipIdDTO,
|
TListProjectMembershipsByOrgMembershipIdDTO,
|
||||||
|
TResendOrgMemberInvitationDTO,
|
||||||
TUpdateOrgDTO,
|
TUpdateOrgDTO,
|
||||||
TUpdateOrgMembershipDTO,
|
TUpdateOrgMembershipDTO,
|
||||||
TVerifyUserToOrgDTO
|
TVerifyUserToOrgDTO
|
||||||
@ -583,6 +585,66 @@ export const orgServiceFactory = ({
|
|||||||
});
|
});
|
||||||
return membership;
|
return membership;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const resendOrgMemberInvitation = async ({
|
||||||
|
orgId,
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId,
|
||||||
|
membershipId
|
||||||
|
}: TResendOrgMemberInvitationDTO) => {
|
||||||
|
const appCfg = getConfig();
|
||||||
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Member);
|
||||||
|
|
||||||
|
const org = await orgDAL.findOrgById(orgId);
|
||||||
|
|
||||||
|
const [inviteeOrgMembership] = await orgDAL.findMembership({
|
||||||
|
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId,
|
||||||
|
[`${TableName.OrgMembership}.id` as "id"]: membershipId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (inviteeOrgMembership.status !== OrgMembershipStatus.Invited) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Organization invitation already accepted"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = await tokenService.createTokenForUser({
|
||||||
|
type: TokenType.TOKEN_EMAIL_ORG_INVITATION,
|
||||||
|
userId: inviteeOrgMembership.userId,
|
||||||
|
orgId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!appCfg.isSmtpConfigured) {
|
||||||
|
return {
|
||||||
|
signupToken: {
|
||||||
|
email: inviteeOrgMembership.email as string,
|
||||||
|
link: `${appCfg.SITE_URL}/signupinvite?token=${token}&to=${inviteeOrgMembership.email}&organization_id=${org?.id}`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await smtpService.sendMail({
|
||||||
|
template: SmtpTemplates.OrgInvite,
|
||||||
|
subjectLine: "Infisical organization invitation",
|
||||||
|
recipients: [inviteeOrgMembership.email as string],
|
||||||
|
substitutions: {
|
||||||
|
inviterFirstName: inviteeOrgMembership.firstName,
|
||||||
|
inviterUsername: inviteeOrgMembership.email,
|
||||||
|
organizationName: org?.name,
|
||||||
|
email: inviteeOrgMembership.email,
|
||||||
|
organizationId: org?.id.toString(),
|
||||||
|
token,
|
||||||
|
callback_url: `${appCfg.SITE_URL}/signupinvite`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { signupToken: undefined };
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Invite user to organization
|
* Invite user to organization
|
||||||
*/
|
*/
|
||||||
@ -626,6 +688,7 @@ export const orgServiceFactory = ({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
if (projectsToInvite.length !== invitedProjects?.length) {
|
if (projectsToInvite.length !== invitedProjects?.length) {
|
||||||
throw new ForbiddenRequestError({
|
throw new ForbiddenRequestError({
|
||||||
message: "Access denied to one or more of the specified projects"
|
message: "Access denied to one or more of the specified projects"
|
||||||
@ -773,13 +836,14 @@ export const orgServiceFactory = ({
|
|||||||
// if there exist no project membership we set is as given by the request
|
// if there exist no project membership we set is as given by the request
|
||||||
for await (const project of projectsToInvite) {
|
for await (const project of projectsToInvite) {
|
||||||
const projectId = project.id;
|
const projectId = project.id;
|
||||||
const { permission: projectPermission } = await permissionService.getProjectPermission(
|
const { permission: projectPermission } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
);
|
actionProjectType: ActionProjectType.Any
|
||||||
|
});
|
||||||
ForbiddenError.from(projectPermission).throwUnlessCan(
|
ForbiddenError.from(projectPermission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Create,
|
ProjectPermissionActions.Create,
|
||||||
ProjectPermissionSub.Member
|
ProjectPermissionSub.Member
|
||||||
@ -1219,6 +1283,7 @@ export const orgServiceFactory = ({
|
|||||||
deleteIncidentContact,
|
deleteIncidentContact,
|
||||||
getOrgGroups,
|
getOrgGroups,
|
||||||
listProjectMembershipsByOrgMembershipId,
|
listProjectMembershipsByOrgMembershipId,
|
||||||
findOrgBySlug
|
findOrgBySlug,
|
||||||
|
resendOrgMemberInvitation
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -35,6 +35,10 @@ export type TInviteUserToOrgDTO = {
|
|||||||
}[];
|
}[];
|
||||||
} & TOrgPermission;
|
} & TOrgPermission;
|
||||||
|
|
||||||
|
export type TResendOrgMemberInvitationDTO = {
|
||||||
|
membershipId: string;
|
||||||
|
} & TOrgPermission;
|
||||||
|
|
||||||
export type TVerifyUserToOrgDTO = {
|
export type TVerifyUserToOrgDTO = {
|
||||||
email: string;
|
email: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user