From 695c4994488ff1dd5217dd807f720c5ac65f1a0c Mon Sep 17 00:00:00 2001 From: = Date: Fri, 6 Dec 2024 14:58:48 +0530 Subject: [PATCH] feat: added type for project and validation check for secret manager specific endpoints --- ...0241205160300_project-split-to-products.ts | 21 +++++++++++++++ backend/src/db/schemas/models.ts | 6 +++++ backend/src/db/schemas/projects.ts | 3 ++- .../access-approval-policy-service.ts | 20 +++++++------- .../dynamic-secret-lease-service.ts | 11 +++++--- .../dynamic-secret/dynamic-secret-service.ts | 11 +++++--- .../ee/services/permission/permission-dal.ts | 16 ++++++++++-- .../services/permission/permission-service.ts | 26 ++++++++++++++++++- .../secret-approval-policy-service.ts | 10 ++++--- .../secret-approval-request-service.ts | 8 +++--- .../secret-rotation-service.ts | 14 ++++++---- .../secret-snapshot-service.ts | 5 ++-- .../src/server/routes/v2/project-router.ts | 9 ++++--- .../integration-auth-service.ts | 14 +++++++--- .../integration/integration-service.ts | 10 ++++--- .../project-env/project-env-service.ts | 10 ++++--- .../src/services/project/project-service.ts | 6 +++-- backend/src/services/project/project-types.ts | 3 ++- .../secret-folder/secret-folder-service.ts | 14 ++++++---- .../secret-import/secret-import-service.ts | 13 +++++++--- .../services/secret-tag/secret-tag-service.ts | 10 ++++--- .../secret-v2-bridge-service.ts | 26 ++++++++++++------- backend/src/services/secret/secret-service.ts | 23 +++++++++++----- 23 files changed, 211 insertions(+), 78 deletions(-) create mode 100644 backend/src/db/migrations/20241205160300_project-split-to-products.ts diff --git a/backend/src/db/migrations/20241205160300_project-split-to-products.ts b/backend/src/db/migrations/20241205160300_project-split-to-products.ts new file mode 100644 index 000000000..a7e0c2268 --- /dev/null +++ b/backend/src/db/migrations/20241205160300_project-split-to-products.ts @@ -0,0 +1,21 @@ +import { Knex } from "knex"; + +import { TableName } from "../schemas"; + +export async function up(knex: Knex): Promise { + const hasTypeColumn = await knex.schema.hasColumn(TableName.Project, "type"); + if (!hasTypeColumn) { + await knex.schema.alterTable(TableName.Project, (t) => { + t.string("type"); + }); + } +} + +export async function down(knex: Knex): Promise { + const hasTypeColumn = await knex.schema.hasColumn(TableName.Project, "type"); + if (hasTypeColumn) { + await knex.schema.alterTable(TableName.Project, (t) => { + t.dropColumn("type"); + }); + } +} diff --git a/backend/src/db/schemas/models.ts b/backend/src/db/schemas/models.ts index 5ec686140..8c50b81b9 100644 --- a/backend/src/db/schemas/models.ts +++ b/backend/src/db/schemas/models.ts @@ -200,3 +200,9 @@ export enum IdentityAuthMethod { OIDC_AUTH = "oidc-auth", JWT_AUTH = "jwt-auth" } + +export enum ProjectType { + SecretManager = "secret-manager", + CertificateManager = "cert-manager", + KMS = "kms" +} diff --git a/backend/src/db/schemas/projects.ts b/backend/src/db/schemas/projects.ts index 5c5f9774b..17976e29c 100644 --- a/backend/src/db/schemas/projects.ts +++ b/backend/src/db/schemas/projects.ts @@ -24,7 +24,8 @@ export const ProjectsSchema = z.object({ auditLogsRetentionDays: z.number().nullable().optional(), kmsSecretManagerKeyId: z.string().uuid().nullable().optional(), kmsSecretManagerEncryptedDataKey: zodBuffer.nullable().optional(), - description: z.string().nullable().optional() + description: z.string().nullable().optional(), + type: z.string().nullable().optional() }); export type TProjects = z.infer; diff --git a/backend/src/ee/services/access-approval-policy/access-approval-policy-service.ts b/backend/src/ee/services/access-approval-policy/access-approval-policy-service.ts index 24436e695..72de6810a 100644 --- a/backend/src/ee/services/access-approval-policy/access-approval-policy-service.ts +++ b/backend/src/ee/services/access-approval-policy/access-approval-policy-service.ts @@ -1,5 +1,6 @@ import { ForbiddenError } from "@casl/ability"; +import { ProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; @@ -86,13 +87,15 @@ export const accessApprovalPolicyServiceFactory = ({ if (!groupApprovers && approvals > userApprovers.length + userApproverNames.length) throw new BadRequestError({ message: "Approvals cannot be greater than approvers" }); - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, project.id, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.SecretApproval @@ -190,14 +193,7 @@ export const accessApprovalPolicyServiceFactory = ({ if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); // Anyone in the project should be able to get the policies. - /* const { permission } = */ await permissionService.getProjectPermission( - actor, - actorId, - project.id, - actorAuthMethod, - actorOrgId - ); - // ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval); + await permissionService.getProjectPermission(actor, actorId, project.id, actorAuthMethod, actorOrgId); const accessApprovalPolicies = await accessApprovalPolicyDAL.find({ projectId: project.id, deletedAt: null }); return accessApprovalPolicies; @@ -241,13 +237,14 @@ export const accessApprovalPolicyServiceFactory = ({ if (!accessApprovalPolicy) { throw new NotFoundError({ message: `Secret approval policy with ID '${policyId}' not found` }); } - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, accessApprovalPolicy.projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval); @@ -324,13 +321,14 @@ export const accessApprovalPolicyServiceFactory = ({ const policy = await accessApprovalPolicyDAL.findById(policyId); if (!policy) throw new NotFoundError({ message: `Secret approval policy with ID '${policyId}' not found` }); - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, policy.projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.SecretApproval diff --git a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts index 82d1604eb..81e76ff71 100644 --- a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts +++ b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts @@ -1,7 +1,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import ms from "ms"; -import { SecretKeyEncoding } from "@app/db/schemas"; +import { ProjectType, SecretKeyEncoding } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { @@ -67,13 +67,14 @@ export const dynamicSecretLeaseServiceFactory = ({ if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); const projectId = project.id; - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionDynamicSecretActions.Lease, subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path }) @@ -146,13 +147,14 @@ export const dynamicSecretLeaseServiceFactory = ({ if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); const projectId = project.id; - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionDynamicSecretActions.Lease, subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path }) @@ -225,13 +227,14 @@ export const dynamicSecretLeaseServiceFactory = ({ if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); const projectId = project.id; - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionDynamicSecretActions.Lease, subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path }) diff --git a/backend/src/ee/services/dynamic-secret/dynamic-secret-service.ts b/backend/src/ee/services/dynamic-secret/dynamic-secret-service.ts index 5eff1cdcf..db60b3e57 100644 --- a/backend/src/ee/services/dynamic-secret/dynamic-secret-service.ts +++ b/backend/src/ee/services/dynamic-secret/dynamic-secret-service.ts @@ -1,6 +1,6 @@ import { ForbiddenError, subject } from "@casl/ability"; -import { SecretKeyEncoding } from "@app/db/schemas"; +import { ProjectType, SecretKeyEncoding } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { @@ -73,13 +73,14 @@ export const dynamicSecretServiceFactory = ({ if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); const projectId = project.id; - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionDynamicSecretActions.CreateRootCredential, subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path }) @@ -144,13 +145,14 @@ export const dynamicSecretServiceFactory = ({ const projectId = project.id; - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionDynamicSecretActions.EditRootCredential, subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path }) @@ -227,13 +229,14 @@ export const dynamicSecretServiceFactory = ({ const projectId = project.id; - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionDynamicSecretActions.DeleteRootCredential, subject(ProjectPermissionSub.DynamicSecrets, { environment: environmentSlug, secretPath: path }) diff --git a/backend/src/ee/services/permission/permission-dal.ts b/backend/src/ee/services/permission/permission-dal.ts index 730ad3bbc..3a3c82414 100644 --- a/backend/src/ee/services/permission/permission-dal.ts +++ b/backend/src/ee/services/permission/permission-dal.ts @@ -269,6 +269,7 @@ export const permissionDALFactory = (db: TDbClient) => { db.ref("value").withSchema(TableName.IdentityMetadata).as("metadataValue"), db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"), db.ref("orgId").withSchema(TableName.Project), + db.ref("type").withSchema(TableName.Project).as("projectType"), db.ref("id").withSchema(TableName.Project).as("projectId") ); @@ -284,13 +285,15 @@ export const permissionDALFactory = (db: TDbClient) => { membershipCreatedAt, groupMembershipCreatedAt, groupMembershipUpdatedAt, - membershipUpdatedAt + membershipUpdatedAt, + projectType }) => ({ orgId, orgAuthEnforced, userId, projectId, username, + projectType, id: membershipId || groupMembershipId, createdAt: membershipCreatedAt || groupMembershipCreatedAt, updatedAt: membershipUpdatedAt || groupMembershipUpdatedAt @@ -449,6 +452,7 @@ export const permissionDALFactory = (db: TDbClient) => { db.ref("id").withSchema(TableName.IdentityProjectMembership).as("membershipId"), db.ref("name").withSchema(TableName.Identity).as("identityName"), db.ref("orgId").withSchema(TableName.Project).as("orgId"), // Now you can select orgId from Project + db.ref("type").withSchema(TableName.Project).as("projectType"), db.ref("createdAt").withSchema(TableName.IdentityProjectMembership).as("membershipCreatedAt"), db.ref("updatedAt").withSchema(TableName.IdentityProjectMembership).as("membershipUpdatedAt"), db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"), @@ -480,7 +484,14 @@ export const permissionDALFactory = (db: TDbClient) => { const permission = sqlNestRelationships({ data: docs, key: "membershipId", - parentMapper: ({ membershipId, membershipCreatedAt, membershipUpdatedAt, orgId, identityName }) => ({ + parentMapper: ({ + membershipId, + membershipCreatedAt, + membershipUpdatedAt, + orgId, + identityName, + projectType + }) => ({ id: membershipId, identityId, username: identityName, @@ -488,6 +499,7 @@ export const permissionDALFactory = (db: TDbClient) => { createdAt: membershipCreatedAt, updatedAt: membershipUpdatedAt, orgId, + projectType, // just a prefilled value orgAuthEnforced: false }), diff --git a/backend/src/ee/services/permission/permission-service.ts b/backend/src/ee/services/permission/permission-service.ts index 13645b8f1..96e189115 100644 --- a/backend/src/ee/services/permission/permission-service.ts +++ b/backend/src/ee/services/permission/permission-service.ts @@ -6,6 +6,7 @@ import handlebars from "handlebars"; import { OrgMembershipRole, ProjectMembershipRole, + ProjectType, ServiceTokenScopes, TIdentityProjectMemberships, TProjectMemberships @@ -255,6 +256,13 @@ export const permissionServiceFactory = ({ return { permission, 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) => userProjectPermission.roles.findIndex( ({ role: slug, customRoleSlug }) => role === slug || slug === customRoleSlug @@ -323,6 +331,13 @@ export const permissionServiceFactory = ({ return { permission, 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) => identityProjectPermission.roles.findIndex( ({ role: slug, customRoleSlug }) => role === slug || slug === customRoleSlug @@ -361,7 +376,14 @@ export const permissionServiceFactory = ({ const scopes = ServiceTokenScopes.parse(serviceToken.scopes || []); return { 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.` + }); + } + } }; }; @@ -370,6 +392,7 @@ export const permissionServiceFactory = ({ permission: MongoAbility; membership: undefined; hasRole: (arg: string) => boolean; + ForbidOnInvalidProjectType: (type: ProjectType) => void; } // service token doesn't have both membership and roles : { permission: MongoAbility; @@ -379,6 +402,7 @@ export const permissionServiceFactory = ({ roles: Array<{ role: string }>; }; hasRole: (role: string) => boolean; + ForbidOnInvalidProjectType: (type: ProjectType) => void; }; const getProjectPermission = async ( diff --git a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts index 4e7bf6d15..b0de6ad75 100644 --- a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts +++ b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-service.ts @@ -1,6 +1,7 @@ import { ForbiddenError } from "@casl/ability"; import picomatch from "picomatch"; +import { ProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; @@ -78,13 +79,14 @@ export const secretApprovalPolicyServiceFactory = ({ if (!groupApprovers.length && approvals > approvers.length) throw new BadRequestError({ message: "Approvals cannot be greater than approvers" }); - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.SecretApproval @@ -191,13 +193,14 @@ export const secretApprovalPolicyServiceFactory = ({ }); } - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, secretApprovalPolicy.projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval); const plan = await licenseService.getPlan(actorOrgId); @@ -285,13 +288,14 @@ export const secretApprovalPolicyServiceFactory = ({ if (!sapPolicy) throw new NotFoundError({ message: `Secret approval policy with ID '${secretPolicyId}' not found` }); - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, sapPolicy.projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.SecretApproval diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts index e1c75b3f9..96ab03b4e 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts @@ -2,6 +2,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import { ProjectMembershipRole, + ProjectType, SecretEncryptionAlgo, SecretKeyEncoding, SecretType, @@ -875,13 +876,14 @@ export const secretApprovalRequestServiceFactory = ({ }: TGenerateSecretApprovalRequestDTO) => { if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Read, subject(ProjectPermissionSub.Secrets, { environment, secretPath }) @@ -1155,14 +1157,14 @@ export const secretApprovalRequestServiceFactory = ({ if (actor === ActorType.SERVICE || actor === ActorType.Machine) throw new BadRequestError({ message: "Cannot use service token or machine token over protected branches" }); - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); - + ForbidOnInvalidProjectType(ProjectType.SecretManager); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); if (!folder) throw new NotFoundError({ diff --git a/backend/src/ee/services/secret-rotation/secret-rotation-service.ts b/backend/src/ee/services/secret-rotation/secret-rotation-service.ts index 6dde2657f..7031d8d12 100644 --- a/backend/src/ee/services/secret-rotation/secret-rotation-service.ts +++ b/backend/src/ee/services/secret-rotation/secret-rotation-service.ts @@ -1,7 +1,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import Ajv from "ajv"; -import { ProjectVersion, TableName } from "@app/db/schemas"; +import { ProjectType, ProjectVersion, TableName } from "@app/db/schemas"; import { decryptSymmetric128BitHexKeyUTF8, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { TProjectPermission } from "@app/lib/types"; @@ -53,13 +53,14 @@ export const secretRotationServiceFactory = ({ actorAuthMethod, projectId }: TProjectPermission) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation); return { @@ -81,13 +82,14 @@ export const secretRotationServiceFactory = ({ secretPath, environment }: TCreateSecretRotationDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.SecretRotation @@ -234,13 +236,14 @@ export const secretRotationServiceFactory = ({ message: "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation." }); - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, doc.projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretRotation); await secretRotationQueue.removeFromQueue(doc.id, doc.interval); await secretRotationQueue.addToQueue(doc.id, doc.interval); @@ -251,13 +254,14 @@ export const secretRotationServiceFactory = ({ const doc = await secretRotationDAL.findById(rotationId); if (!doc) throw new NotFoundError({ message: `Rotation with ID '${rotationId}' not found` }); - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, doc.projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, ProjectPermissionSub.SecretRotation diff --git a/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts b/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts index 481123896..2526facb7 100644 --- a/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts +++ b/backend/src/ee/services/secret-snapshot/secret-snapshot-service.ts @@ -1,6 +1,6 @@ import { ForbiddenError, subject } from "@casl/ability"; -import { TableName, TSecretTagJunctionInsert, TSecretV2TagJunctionInsert } from "@app/db/schemas"; +import { ProjectType, TableName, TSecretTagJunctionInsert, TSecretV2TagJunctionInsert } from "@app/db/schemas"; import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto"; import { InternalServerError, NotFoundError } from "@app/lib/errors"; import { groupBy } from "@app/lib/fn"; @@ -322,13 +322,14 @@ export const secretSnapshotServiceFactory = ({ if (!snapshot) throw new NotFoundError({ message: `Snapshot with ID '${snapshotId}' not found` }); const shouldUseBridge = snapshot.projectVersion === 3; - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, snapshot.projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, ProjectPermissionSub.SecretRollback diff --git a/backend/src/server/routes/v2/project-router.ts b/backend/src/server/routes/v2/project-router.ts index 0df88e38c..0e9da8d17 100644 --- a/backend/src/server/routes/v2/project-router.ts +++ b/backend/src/server/routes/v2/project-router.ts @@ -5,7 +5,8 @@ import { CertificatesSchema, PkiAlertsSchema, PkiCollectionsSchema, - ProjectKeysSchema + ProjectKeysSchema, + ProjectType } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { InfisicalProjectTemplate } from "@app/ee/services/project-template/project-template-types"; @@ -159,7 +160,8 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { template: slugSchema({ field: "Template Name", max: 64 }) .optional() .default(InfisicalProjectTemplate.Default) - .describe(PROJECTS.CREATE.template) + .describe(PROJECTS.CREATE.template), + type: z.nativeEnum(ProjectType).default(ProjectType.SecretManager) }), response: { 200: z.object({ @@ -178,7 +180,8 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { workspaceDescription: req.body.projectDescription, slug: req.body.slug, kmsKeyId: req.body.kmsKeyId, - template: req.body.template + template: req.body.template, + type: req.body.type }); await server.services.telemetry.sendPostHogEvents({ diff --git a/backend/src/services/integration-auth/integration-auth-service.ts b/backend/src/services/integration-auth/integration-auth-service.ts index 42a3f038b..b4bbbd7cb 100644 --- a/backend/src/services/integration-auth/integration-auth-service.ts +++ b/backend/src/services/integration-auth/integration-auth-service.ts @@ -4,7 +4,13 @@ import { Octokit } from "@octokit/rest"; import { Client as OctopusClient, SpaceRepository as OctopusSpaceRepository } from "@octopusdeploy/api-client"; import AWS from "aws-sdk"; -import { SecretEncryptionAlgo, SecretKeyEncoding, TIntegrationAuths, TIntegrationAuthsInsert } from "@app/db/schemas"; +import { + ProjectType, + SecretEncryptionAlgo, + SecretKeyEncoding, + TIntegrationAuths, + TIntegrationAuthsInsert +} from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { getConfig } from "@app/lib/config/env"; @@ -145,13 +151,14 @@ export const integrationAuthServiceFactory = ({ if (!Object.values(Integrations).includes(integration as Integrations)) throw new BadRequestError({ message: "Invalid integration" }); - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); const tokenExchange = await exchangeCode({ integration, code, url, installationId }); @@ -254,13 +261,14 @@ export const integrationAuthServiceFactory = ({ if (!Object.values(Integrations).includes(integration as Integrations)) throw new BadRequestError({ message: "Invalid integration" }); - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); const updateDoc: TIntegrationAuthsInsert = { diff --git a/backend/src/services/integration/integration-service.ts b/backend/src/services/integration/integration-service.ts index a990b1ca6..36a4156e7 100644 --- a/backend/src/services/integration/integration-service.ts +++ b/backend/src/services/integration/integration-service.ts @@ -1,5 +1,6 @@ import { ForbiddenError, subject } from "@casl/ability"; +import { ProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { NotFoundError } from "@app/lib/errors"; @@ -80,13 +81,14 @@ export const integrationServiceFactory = ({ if (!integrationAuth) throw new NotFoundError({ message: `Integration auth with ID '${integrationAuthId}' not found` }); - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, integrationAuth.projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations); ForbiddenError.from(permission).throwUnlessCan( @@ -158,13 +160,14 @@ export const integrationServiceFactory = ({ const integration = await integrationDAL.findById(id); if (!integration) throw new NotFoundError({ message: `Integration with ID '${id}' not found` }); - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, integration.projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations); const newEnvironment = environment || integration.environment.slug; @@ -293,13 +296,14 @@ export const integrationServiceFactory = ({ const integration = await integrationDAL.findById(id); if (!integration) throw new NotFoundError({ message: `Integration with ID '${id}' not found` }); - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, integration.projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations); const integrationAuth = await integrationAuthDAL.findById(integration.integrationAuthId); diff --git a/backend/src/services/project-env/project-env-service.ts b/backend/src/services/project-env/project-env-service.ts index a54e8de43..5c3fdba32 100644 --- a/backend/src/services/project-env/project-env-service.ts +++ b/backend/src/services/project-env/project-env-service.ts @@ -1,5 +1,6 @@ import { ForbiddenError } from "@casl/ability"; +import { ProjectType } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; @@ -41,13 +42,14 @@ export const projectEnvServiceFactory = ({ name, slug }: TCreateEnvDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Environments); const lock = await keyStore @@ -129,13 +131,14 @@ export const projectEnvServiceFactory = ({ id, position }: TUpdateEnvDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Environments); const lock = await keyStore @@ -192,13 +195,14 @@ export const projectEnvServiceFactory = ({ }; const deleteEnvironment = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod, id }: TDeleteEnvDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Environments); const lock = await keyStore diff --git a/backend/src/services/project/project-service.ts b/backend/src/services/project/project-service.ts index e9f750d9d..bdec8bad5 100644 --- a/backend/src/services/project/project-service.ts +++ b/backend/src/services/project/project-service.ts @@ -1,7 +1,7 @@ import { ForbiddenError } from "@casl/ability"; import slugify from "@sindresorhus/slugify"; -import { ProjectMembershipRole, ProjectVersion, TProjectEnvironments } from "@app/db/schemas"; +import { ProjectMembershipRole, ProjectType, ProjectVersion, TProjectEnvironments } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; @@ -153,7 +153,8 @@ export const projectServiceFactory = ({ kmsKeyId, tx: trx, createDefaultEnvs = true, - template = InfisicalProjectTemplate.Default + template = InfisicalProjectTemplate.Default, + type = ProjectType.SecretManager }: TCreateProjectDTO) => { const organization = await orgDAL.findOne({ id: actorOrgId }); @@ -206,6 +207,7 @@ export const projectServiceFactory = ({ const project = await projectDAL.create( { name: workspaceName, + type, description: workspaceDescription, orgId: organization.id, slug: projectSlug || slugify(`${workspaceName}-${alphaNumericNanoId(4)}`), diff --git a/backend/src/services/project/project-types.ts b/backend/src/services/project/project-types.ts index b826f2a6a..b9486f984 100644 --- a/backend/src/services/project/project-types.ts +++ b/backend/src/services/project/project-types.ts @@ -1,6 +1,6 @@ import { Knex } from "knex"; -import { TProjectKeys } from "@app/db/schemas"; +import { ProjectType, TProjectKeys } from "@app/db/schemas"; import { TProjectPermission } from "@app/lib/types"; import { ActorAuthMethod, ActorType } from "../auth/auth-type"; @@ -35,6 +35,7 @@ export type TCreateProjectDTO = { createDefaultEnvs?: boolean; template?: string; tx?: Knex; + type?: ProjectType; }; export type TDeleteProjectBySlugDTO = { diff --git a/backend/src/services/secret-folder/secret-folder-service.ts b/backend/src/services/secret-folder/secret-folder-service.ts index d787520a2..99bd81d6d 100644 --- a/backend/src/services/secret-folder/secret-folder-service.ts +++ b/backend/src/services/secret-folder/secret-folder-service.ts @@ -2,7 +2,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import path from "path"; import { v4 as uuidv4, validate as uuidValidate } from "uuid"; -import { TSecretFoldersInsert } from "@app/db/schemas"; +import { ProjectType, TSecretFoldersInsert } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service"; @@ -52,13 +52,14 @@ export const secretFolderServiceFactory = ({ environment, path: secretPath }: TCreateFolderDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, @@ -150,13 +151,14 @@ export const secretFolderServiceFactory = ({ throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); } - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, project.id, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); folders.forEach(({ environment, path: secretPath }) => { ForbiddenError.from(permission).throwUnlessCan( @@ -259,13 +261,14 @@ export const secretFolderServiceFactory = ({ path: secretPath, id }: TUpdateFolderDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, @@ -339,13 +342,14 @@ export const secretFolderServiceFactory = ({ path: secretPath, idOrName }: TDeleteFolderDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, diff --git a/backend/src/services/secret-import/secret-import-service.ts b/backend/src/services/secret-import/secret-import-service.ts index 25e78fb65..bb2c54372 100644 --- a/backend/src/services/secret-import/secret-import-service.ts +++ b/backend/src/services/secret-import/secret-import-service.ts @@ -2,7 +2,7 @@ import path from "node:path"; import { ForbiddenError, subject } from "@casl/ability"; -import { TableName } from "@app/db/schemas"; +import { ProjectType, TableName } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; @@ -73,13 +73,14 @@ export const secretImportServiceFactory = ({ isReplication, path: secretPath }: TCreateSecretImportDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); // check if user has permission to import into destination path ForbiddenError.from(permission).throwUnlessCan( @@ -189,13 +190,15 @@ export const secretImportServiceFactory = ({ data, id }: TUpdateSecretImportDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, subject(ProjectPermissionSub.SecretImports, { environment, secretPath }) @@ -283,13 +286,15 @@ export const secretImportServiceFactory = ({ actorAuthMethod, id }: TDeleteSecretImportDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, subject(ProjectPermissionSub.SecretImports, { environment, secretPath }) diff --git a/backend/src/services/secret-tag/secret-tag-service.ts b/backend/src/services/secret-tag/secret-tag-service.ts index 6cae3997a..02d2b5af0 100644 --- a/backend/src/services/secret-tag/secret-tag-service.ts +++ b/backend/src/services/secret-tag/secret-tag-service.ts @@ -1,5 +1,6 @@ import { ForbiddenError } from "@casl/ability"; +import { ProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; @@ -23,7 +24,7 @@ export type TSecretTagServiceFactory = ReturnType { const createTag = async ({ slug, actor, color, actorId, actorOrgId, actorAuthMethod, projectId }: TCreateTagDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, @@ -31,6 +32,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Tags); + ForbidOnInvalidProjectType(ProjectType.SecretManager); const existingTag = await secretTagDAL.findOne({ slug, projectId }); if (existingTag) throw new BadRequestError({ message: "Tag already exist" }); @@ -54,7 +56,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe if (existingTag && existingTag.id !== tag.id) throw new BadRequestError({ message: "Tag already exist" }); } - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, tag.projectId, @@ -62,6 +64,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Tags); + ForbidOnInvalidProjectType(ProjectType.SecretManager); const updatedTag = await secretTagDAL.updateById(tag.id, { color, slug }); return updatedTag; @@ -71,7 +74,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe const tag = await secretTagDAL.findById(id); if (!tag) throw new NotFoundError({ message: `Tag with ID '${id}' not found` }); - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, tag.projectId, @@ -79,6 +82,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Tags); + ForbidOnInvalidProjectType(ProjectType.SecretManager); const deletedTag = await secretTagDAL.deleteById(tag.id); return deletedTag; diff --git a/backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts b/backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts index 0803bda35..e13a77432 100644 --- a/backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts +++ b/backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts @@ -1,7 +1,7 @@ import { ForbiddenError, PureAbility, subject } from "@casl/ability"; import { z } from "zod"; -import { ProjectMembershipRole, SecretsV2Schema, SecretType, TableName } from "@app/db/schemas"; +import { ProjectMembershipRole, ProjectType, SecretsV2Schema, SecretType, TableName } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service"; @@ -188,13 +188,14 @@ export const secretV2BridgeServiceFactory = ({ secretPath, ...inputSecret }: TCreateSecretDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); if (!folder) @@ -310,13 +311,14 @@ export const secretV2BridgeServiceFactory = ({ secretPath, ...inputSecret }: TUpdateSecretDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); if (inputSecret.newSecretName === "") { throw new BadRequestError({ message: "New secret name cannot be empty" }); @@ -494,13 +496,14 @@ export const secretV2BridgeServiceFactory = ({ secretPath, ...inputSecret }: TDeleteSecretDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); if (!folder) @@ -1081,13 +1084,14 @@ export const secretV2BridgeServiceFactory = ({ projectId, secrets: inputSecrets }: TCreateManySecretDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); if (!folder) @@ -1221,13 +1225,14 @@ export const secretV2BridgeServiceFactory = ({ secretPath, secrets: inputSecrets }: TUpdateManySecretDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); if (!folder) @@ -1427,13 +1432,14 @@ export const secretV2BridgeServiceFactory = ({ actorAuthMethod, actorOrgId }: TDeleteManySecretDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); if (!folder) @@ -1569,13 +1575,14 @@ export const secretV2BridgeServiceFactory = ({ actorOrgId, actorAuthMethod }: TBackFillSecretReferencesDTO) => { - const { hasRole } = await permissionService.getProjectPermission( + const { hasRole, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); if (!hasRole(ProjectMembershipRole.Admin)) throw new ForbiddenRequestError({ message: "Only admins are allowed to take this action" }); @@ -1616,13 +1623,14 @@ export const secretV2BridgeServiceFactory = ({ actorAuthMethod, actorOrgId }: TMoveSecretsDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); const sourceFolder = await folderDAL.findBySecretPath(projectId, sourceEnvironment, sourceSecretPath); if (!sourceFolder) { diff --git a/backend/src/services/secret/secret-service.ts b/backend/src/services/secret/secret-service.ts index d62d09f7a..6f058e023 100644 --- a/backend/src/services/secret/secret-service.ts +++ b/backend/src/services/secret/secret-service.ts @@ -4,6 +4,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import { ProjectMembershipRole, + ProjectType, ProjectUpgradeStatus, SecretEncryptionAlgo, SecretKeyEncoding, @@ -186,13 +187,15 @@ export const secretServiceFactory = ({ projectId, ...inputSecret }: TCreateSecretDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -301,13 +304,15 @@ export const secretServiceFactory = ({ projectId, ...inputSecret }: TUpdateSecretDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -443,13 +448,15 @@ export const secretServiceFactory = ({ projectId, ...inputSecret }: TDeleteSecretDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -732,13 +739,14 @@ export const secretServiceFactory = ({ projectId, secrets: inputSecrets }: TCreateBulkSecretDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Create, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -817,13 +825,15 @@ export const secretServiceFactory = ({ projectId, secrets: inputSecrets }: TUpdateBulkSecretDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); + ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Edit, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) @@ -923,13 +933,14 @@ export const secretServiceFactory = ({ actorAuthMethod, actorOrgId }: TDeleteBulkSecretDTO) => { - const { permission } = await permissionService.getProjectPermission( + const { permission, ForbidOnInvalidProjectType } = await permissionService.getProjectPermission( actor, actorId, projectId, actorAuthMethod, actorOrgId ); + ForbidOnInvalidProjectType(ProjectType.SecretManager); ForbiddenError.from(permission).throwUnlessCan( ProjectPermissionActions.Delete, subject(ProjectPermissionSub.Secrets, { environment, secretPath: path })