diff --git a/backend/src/db/migrations/20250722152841_add-policies-environments-table.ts b/backend/src/db/migrations/20250722152841_add-policies-environments-table.ts index 3c2edc365..450a6c4d6 100644 --- a/backend/src/db/migrations/20250722152841_add-policies-environments-table.ts +++ b/backend/src/db/migrations/20250722152841_add-policies-environments-table.ts @@ -14,6 +14,7 @@ export async function up(knex: Knex): Promise { t.uuid("envId").notNullable(); t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE"); t.timestamps(true, true, true); + t.unique(["policyId", "envId"]); }); } if (!(await knex.schema.hasTable(TableName.SecretApprovalPolicyEnvironment))) { @@ -24,6 +25,7 @@ export async function up(knex: Knex): Promise { t.uuid("envId").notNullable(); t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE"); t.timestamps(true, true, true); + t.unique(["policyId", "envId"]); }); } diff --git a/backend/src/ee/routes/v1/access-approval-policy-router.ts b/backend/src/ee/routes/v1/access-approval-policy-router.ts index c01d2fc28..ef44344de 100644 --- a/backend/src/ee/routes/v1/access-approval-policy-router.ts +++ b/backend/src/ee/routes/v1/access-approval-policy-router.ts @@ -92,7 +92,8 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi actorOrgId: req.permission.orgId, ...req.body, projectSlug: req.body.projectSlug, - name: req.body.name ?? `${req.body.environment || req.body.environments?.join("-")}-${nanoid(3)}`, + name: + req.body.name ?? `${req.body.environment || req.body.environments?.join("-").substring(0, 250)}-${nanoid(3)}`, enforcementLevel: req.body.enforcementLevel }); return { approval }; diff --git a/backend/src/ee/services/access-approval-policy/access-approval-policy-dal.ts b/backend/src/ee/services/access-approval-policy/access-approval-policy-dal.ts index e01b51a5d..9baf762d6 100644 --- a/backend/src/ee/services/access-approval-policy/access-approval-policy-dal.ts +++ b/backend/src/ee/services/access-approval-policy/access-approval-policy-dal.ts @@ -144,7 +144,7 @@ export interface TAccessApprovalPolicyDALFactory } | undefined >; - findPoliciesByEnvIdAndSecretPath: ( + findPolicyByEnvIdAndSecretPath: ( { envIds, secretPath }: { envIds: string[]; secretPath: string }, tx?: Knex ) => Promise<{ @@ -399,17 +399,17 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient): TAccessApprovalPo void qb.where(`${TableName.AccessApprovalPolicy}.id`, "=", customFilter.policyId); } }) - .where((qb) => { - if (customFilter?.envId) { - void qb.where(`${TableName.AccessApprovalPolicyEnvironment}.envId`, "=", customFilter.envId); - } - }) .join( TableName.AccessApprovalPolicyEnvironment, `${TableName.AccessApprovalPolicy}.id`, `${TableName.AccessApprovalPolicyEnvironment}.policyId` ) .join(TableName.Environment, `${TableName.AccessApprovalPolicyEnvironment}.envId`, `${TableName.Environment}.id`) + .where((qb) => { + if (customFilter?.envId) { + void qb.where(`${TableName.AccessApprovalPolicyEnvironment}.envId`, "=", customFilter.envId); + } + }) .leftJoin( TableName.AccessApprovalPolicyApprover, `${TableName.AccessApprovalPolicy}.id`, @@ -612,7 +612,7 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient): TAccessApprovalPo } }; - const findPoliciesByEnvIdAndSecretPath: TAccessApprovalPolicyDALFactory["findPoliciesByEnvIdAndSecretPath"] = async ( + const findPolicyByEnvIdAndSecretPath: TAccessApprovalPolicyDALFactory["findPolicyByEnvIdAndSecretPath"] = async ( { envIds, secretPath }, tx ) => { @@ -677,7 +677,7 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient): TAccessApprovalPo }); return formattedDocs?.[0]; } catch (error) { - throw new DatabaseError({ error, name: "FindPoliciesByEnvIdAndSecretPath" }); + throw new DatabaseError({ error, name: "findPolicyByEnvIdAndSecretPath" }); } }; @@ -687,6 +687,6 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient): TAccessApprovalPo findById, softDeleteById, findLastValidPolicy, - findPoliciesByEnvIdAndSecretPath + findPolicyByEnvIdAndSecretPath }; }; diff --git a/backend/src/ee/services/access-approval-policy/access-approval-policy-environment-dal.ts b/backend/src/ee/services/access-approval-policy/access-approval-policy-environment-dal.ts index 8485df036..f0d8079cf 100644 --- a/backend/src/ee/services/access-approval-policy/access-approval-policy-environment-dal.ts +++ b/backend/src/ee/services/access-approval-policy/access-approval-policy-environment-dal.ts @@ -3,14 +3,14 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; import { TableName } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; -import { ormify, selectAllTableCols } from "@app/lib/knex"; +import { buildFindFilter, ormify, selectAllTableCols } from "@app/lib/knex"; export type TAccessApprovalPolicyEnvironmentDALFactory = ReturnType; export const accessApprovalPolicyEnvironmentDALFactory = (db: TDbClient) => { const accessApprovalPolicyEnvironmentOrm = ormify(db, TableName.AccessApprovalPolicyEnvironment); - const findAvailablePoliciesIds = async (envId: string, tx?: Knex) => { + const findAvailablePoliciesByEnvId = async (envId: string, tx?: Knex) => { try { const docs = await (tx || db.replicaNode())(TableName.AccessApprovalPolicyEnvironment) .join( @@ -18,14 +18,15 @@ export const accessApprovalPolicyEnvironmentDALFactory = (db: TDbClient) => { `${TableName.AccessApprovalPolicyEnvironment}.policyId`, `${TableName.AccessApprovalPolicy}.id` ) - .where({ [`${TableName.AccessApprovalPolicyEnvironment}.envId` as "envId"]: envId }) + // eslint-disable-next-line @typescript-eslint/no-misused-promises + .where(buildFindFilter({ envId }, TableName.AccessApprovalPolicyEnvironment)) .whereNull(`${TableName.AccessApprovalPolicy}.deletedAt`) .select(selectAllTableCols(TableName.AccessApprovalPolicyEnvironment)); return docs; } catch (error) { - throw new DatabaseError({ error, name: "findAvailablePoliciesIds" }); + throw new DatabaseError({ error, name: "findAvailablePoliciesByEnvId" }); } }; - return { ...accessApprovalPolicyEnvironmentOrm, findAvailablePoliciesIds }; + return { ...accessApprovalPolicyEnvironmentOrm, findAvailablePoliciesByEnvId }; }; 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 693198f0e..0282175df 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 @@ -77,9 +77,9 @@ export const accessApprovalPolicyServiceFactory = ({ if (!envId && !envIds) { throw new BadRequestError({ message: "Must provide either envId or envIds" }); } - const policy = await accessApprovalPolicyDAL.findPoliciesByEnvIdAndSecretPath({ + const policy = await accessApprovalPolicyDAL.findPolicyByEnvIdAndSecretPath({ secretPath, - envIds: envId ? [envId] : envIds || [] + envIds: envId ? [envId] : (envIds as string[]) }); return policyId ? policy && policy.id !== policyId : Boolean(policy); }; diff --git a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-dal.ts b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-dal.ts index c19286105..3212fb902 100644 --- a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-dal.ts +++ b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-dal.ts @@ -265,7 +265,7 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => { return softDeletedPolicy; }; - const findPoliciesByEnvIdAndSecretPath = async ( + const findPolicyByEnvIdAndSecretPath = async ( { envIds, secretPath }: { envIds: string[]; secretPath: string }, tx?: Knex ) => { @@ -330,9 +330,9 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => { }); return formattedDocs?.[0]; } catch (error) { - throw new DatabaseError({ error, name: "FindPoliciesByEnvIdAndSecretPath" }); + throw new DatabaseError({ error, name: "findPolicyByEnvIdAndSecretPath" }); } }; - return { ...secretApprovalPolicyOrm, findById, find, softDeleteById, findPoliciesByEnvIdAndSecretPath }; + return { ...secretApprovalPolicyOrm, findById, find, softDeleteById, findPolicyByEnvIdAndSecretPath }; }; diff --git a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-environment-dal.ts b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-environment-dal.ts index 58c2173de..d12ace04c 100644 --- a/backend/src/ee/services/secret-approval-policy/secret-approval-policy-environment-dal.ts +++ b/backend/src/ee/services/secret-approval-policy/secret-approval-policy-environment-dal.ts @@ -3,14 +3,14 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; import { TableName } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; -import { ormify, selectAllTableCols } from "@app/lib/knex"; +import { buildFindFilter, ormify, selectAllTableCols } from "@app/lib/knex"; export type TSecretApprovalPolicyEnvironmentDALFactory = ReturnType; export const secretApprovalPolicyEnvironmentDALFactory = (db: TDbClient) => { const secretApprovalPolicyEnvironmentOrm = ormify(db, TableName.SecretApprovalPolicyEnvironment); - const findAvailablePoliciesIds = async (envId: string, tx?: Knex) => { + const findAvailablePoliciesByEnvId = async (envId: string, tx?: Knex) => { try { const docs = await (tx || db.replicaNode())(TableName.SecretApprovalPolicyEnvironment) .join( @@ -18,14 +18,15 @@ export const secretApprovalPolicyEnvironmentDALFactory = (db: TDbClient) => { `${TableName.SecretApprovalPolicyEnvironment}.policyId`, `${TableName.SecretApprovalPolicy}.id` ) - .where({ [`${TableName.SecretApprovalPolicyEnvironment}.envId` as "envId"]: envId }) + // eslint-disable-next-line @typescript-eslint/no-misused-promises + .where(buildFindFilter({ envId }, TableName.SecretApprovalPolicyEnvironment)) .whereNull(`${TableName.SecretApprovalPolicy}.deletedAt`) .select(selectAllTableCols(TableName.SecretApprovalPolicyEnvironment)); return docs; } catch (error) { - throw new DatabaseError({ error, name: "findAvailablePoliciesIds" }); + throw new DatabaseError({ error, name: "findAvailablePoliciesByEnvId" }); } }; - return { ...secretApprovalPolicyEnvironmentOrm, findAvailablePoliciesIds }; + return { ...secretApprovalPolicyEnvironmentOrm, findAvailablePoliciesByEnvId }; }; 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 b6392d018..e3d54bcf5 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 @@ -72,7 +72,7 @@ export const secretApprovalPolicyServiceFactory = ({ if (!envIds && !envId) { throw new BadRequestError({ message: "At least one environment should be provided" }); } - const policy = await secretApprovalPolicyDAL.findPoliciesByEnvIdAndSecretPath({ + const policy = await secretApprovalPolicyDAL.findPolicyByEnvIdAndSecretPath({ envIds: envId ? [envId] : envIds || [], secretPath }); diff --git a/backend/src/services/project-env/project-env-service.ts b/backend/src/services/project-env/project-env-service.ts index 9d4fb86ee..b76e93fed 100644 --- a/backend/src/services/project-env/project-env-service.ts +++ b/backend/src/services/project-env/project-env-service.ts @@ -21,8 +21,8 @@ type TProjectEnvServiceFactoryDep = { permissionService: Pick; licenseService: Pick; keyStore: Pick; - accessApprovalPolicyEnvironmentDAL: Pick; - secretApprovalPolicyEnvironmentDAL: Pick; + accessApprovalPolicyEnvironmentDAL: Pick; + secretApprovalPolicyEnvironmentDAL: Pick; }; export type TProjectEnvServiceFactory = ReturnType; @@ -222,15 +222,15 @@ export const projectEnvServiceFactory = ({ } const env = await projectEnvDAL.transaction(async (tx) => { - const secretApprovalRequest = await secretApprovalPolicyEnvironmentDAL.findAvailablePoliciesIds(id, tx); - if (secretApprovalRequest.length > 0) { + const secretApprovalPolicies = await secretApprovalPolicyEnvironmentDAL.findAvailablePoliciesByEnvId(id, tx); + if (secretApprovalPolicies.length > 0) { throw new BadRequestError({ message: "Environment is in use by a secret approval policy", name: "DeleteEnvironment" }); } - const accessApprovalPolicy = await accessApprovalPolicyEnvironmentDAL.findAvailablePoliciesIds(id, tx); - if (accessApprovalPolicy.length > 0) { + const accessApprovalPolicies = await accessApprovalPolicyEnvironmentDAL.findAvailablePoliciesByEnvId(id, tx); + if (accessApprovalPolicies.length > 0) { throw new BadRequestError({ message: "Environment is in use by an access approval policy", name: "DeleteEnvironment" diff --git a/frontend/src/hooks/api/accessApproval/types.ts b/frontend/src/hooks/api/accessApproval/types.ts index b07615372..bc569165b 100644 --- a/frontend/src/hooks/api/accessApproval/types.ts +++ b/frontend/src/hooks/api/accessApproval/types.ts @@ -8,7 +8,6 @@ export type TAccessApprovalPolicy = { name: string; approvals: number; secretPath: string; - envId: string; workspace: string; environments: WorkspaceEnv[]; projectId: string; diff --git a/frontend/src/hooks/api/secretApproval/types.ts b/frontend/src/hooks/api/secretApproval/types.ts index 0f0b604f4..8fd86624d 100644 --- a/frontend/src/hooks/api/secretApproval/types.ts +++ b/frontend/src/hooks/api/secretApproval/types.ts @@ -5,7 +5,6 @@ export type TSecretApprovalPolicy = { id: string; workspace: string; name: string; - envId: string; environments: WorkspaceEnv[]; secretPath?: string; approvals: number;