♻️ prevent deleting resource policies if they have attached resources

This commit is contained in:
Fred KISSIE
2026-03-07 01:12:10 +01:00
parent 884482ec35
commit f8e18de2fc
3 changed files with 44 additions and 18 deletions

View File

@@ -102,6 +102,12 @@ export const resources = pgTable("resources", {
() => resourcePolicies.resourcePolicyId, () => resourcePolicies.resourcePolicyId,
{ onDelete: "set null" } { onDelete: "set null" }
), ),
defaultResourcePolicyId: integer("defaultResourcePolicyId").references(
() => resourcePolicies.resourcePolicyId,
{
onDelete: "restrict"
}
),
resourceGuid: varchar("resourceGuid", { length: 36 }) resourceGuid: varchar("resourceGuid", { length: 36 })
.unique() .unique()
.notNull() .notNull()

View File

@@ -344,6 +344,17 @@ authenticated.get(
approval.countApprovals approval.countApprovals
); );
authenticated.delete(
"/resource-policy/:resourcePolicyId",
verifyResourcePolicyAccess,
verifyValidLicense,
// verifyValidSubscription(tierMatrix.loginPageDomain), // todo: use the correct subscription ?
verifyLimits,
verifyUserHasAction(ActionsEnum.deleteResourcePolicy),
logActionAudit(ActionsEnum.deleteResourcePolicy),
policy.deleteResourcePolicy
);
authenticated.get( authenticated.get(
"/org/:orgId/resource-policies", "/org/:orgId/resource-policies",
verifyValidLicense, verifyValidLicense,
@@ -355,18 +366,6 @@ authenticated.get(
policy.listResourcePolicies policy.listResourcePolicies
); );
authenticated.delete(
"/resource-policy/:resourcePolicyId",
verifyResourcePolicyAccess,
verifyValidLicense,
// verifyValidSubscription(tierMatrix.loginPageDomain), // todo: use the correct subscription ?
verifyOrgAccess,
verifyLimits,
verifyUserHasAction(ActionsEnum.deleteResourcePolicy),
logActionAudit(ActionsEnum.deleteResourcePolicy),
policy.deleteResourcePolicy
);
authenticated.post( authenticated.post(
"/org/:orgId/resource-policy", "/org/:orgId/resource-policy",
verifyValidLicense, verifyValidLicense,

View File

@@ -11,7 +11,7 @@
* This file is not licensed under the AGPLv3. * This file is not licensed under the AGPLv3.
*/ */
import { db, resourcePolicies } from "@server/db"; import { db, resourcePolicies, resources } from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import logger from "@server/logger"; import logger from "@server/logger";
import { OpenAPITags, registry } from "@server/openApi"; import { OpenAPITags, registry } from "@server/openApi";
@@ -56,12 +56,12 @@ export async function deleteResourcePolicy(
const { resourcePolicyId } = parsedParams.data; const { resourcePolicyId } = parsedParams.data;
const [deletedResource] = await db const [existingResource] = await db
.delete(resourcePolicies) .select()
.where(eq(resourcePolicies.resourcePolicyId, resourcePolicyId)) .from(resourcePolicies)
.returning(); .where(eq(resourcePolicies.resourcePolicyId, resourcePolicyId));
if (!deletedResource) { if (!existingResource) {
return next( return next(
createHttpError( createHttpError(
HttpCode.NOT_FOUND, HttpCode.NOT_FOUND,
@@ -70,6 +70,27 @@ export async function deleteResourcePolicy(
); );
} }
const totalAffectedResources = await db.$count(
db
.select()
.from(resources)
.where(eq(resources.resourcePolicyId, resourcePolicyId))
);
if (totalAffectedResources > 0) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
`Cannot delete Policy '${existingResource.name}' as it's being used by at least one resource`
)
);
}
// delete policy
await db
.delete(resourcePolicies)
.where(eq(resourcePolicies.resourcePolicyId, resourcePolicyId));
return response(res, { return response(res, {
data: null, data: null,
success: true, success: true,