Resource policy api backward compatability

This commit is contained in:
Owen
2026-06-04 21:59:34 -07:00
parent def1e9c851
commit 84fef5f1d6
16 changed files with 851 additions and 239 deletions
+34 -1
View File
@@ -1,7 +1,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db, resources } from "@server/db"; import { db, resources } from "@server/db";
import { roleResources, roles } from "@server/db"; import { roleResources, roles, rolePolicies } from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -131,6 +131,38 @@ export async function addRoleToResource(
); );
} }
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
if (isInlinePolicy) {
const policyId = resource.defaultResourcePolicyId!;
// Check if role already exists in the inline policy
const existingEntry = await db
.select()
.from(rolePolicies)
.where(
and(
eq(rolePolicies.resourcePolicyId, policyId),
eq(rolePolicies.roleId, roleId)
)
);
if (existingEntry.length > 0) {
return next(
createHttpError(
HttpCode.CONFLICT,
"Role already assigned to resource"
)
);
}
await db.insert(rolePolicies).values({
roleId,
resourcePolicyId: policyId
});
} else {
// Check if role already exists in resource // Check if role already exists in resource
const existingEntry = await db const existingEntry = await db
.select() .select()
@@ -155,6 +187,7 @@ export async function addRoleToResource(
roleId, roleId,
resourceId resourceId
}); });
}
return response(res, { return response(res, {
data: {}, data: {},
+34 -1
View File
@@ -1,7 +1,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db, resources } from "@server/db"; import { db, resources } from "@server/db";
import { userResources } from "@server/db"; import { userResources, userPolicies } from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -103,6 +103,38 @@ export async function addUserToResource(
); );
} }
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
if (isInlinePolicy) {
const policyId = resource.defaultResourcePolicyId!;
// Check if user already exists in the inline policy
const existingEntry = await db
.select()
.from(userPolicies)
.where(
and(
eq(userPolicies.resourcePolicyId, policyId),
eq(userPolicies.userId, userId)
)
);
if (existingEntry.length > 0) {
return next(
createHttpError(
HttpCode.CONFLICT,
"User already assigned to resource"
)
);
}
await db.insert(userPolicies).values({
userId,
resourcePolicyId: policyId
});
} else {
// Check if user already exists in resource // Check if user already exists in resource
const existingEntry = await db const existingEntry = await db
.select() .select()
@@ -127,6 +159,7 @@ export async function addUserToResource(
userId, userId,
resourceId resourceId
}); });
}
return response(res, { return response(res, {
data: {}, data: {},
+29 -1
View File
@@ -1,7 +1,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db } from "@server/db"; import { db } from "@server/db";
import { resourceRules, resources } from "@server/db"; import { resourceRules, resourcePolicyRules, resources } from "@server/db";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
@@ -153,6 +153,34 @@ export async function createResourceRule(
} }
} }
// Create the new resource rule
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
if (isInlinePolicy) {
const policyId = resource.defaultResourcePolicyId!;
const [newRule] = await db
.insert(resourcePolicyRules)
.values({
resourcePolicyId: policyId,
action,
match,
value,
priority,
enabled
})
.returning();
return response(res, {
data: newRule,
success: true,
error: false,
message: "Resource rule created successfully",
status: HttpCode.CREATED
});
}
// Create the new resource rule // Create the new resource rule
const [newRule] = await db const [newRule] = await db
.insert(resourceRules) .insert(resourceRules)
+43 -1
View File
@@ -1,7 +1,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db } from "@server/db"; import { db } from "@server/db";
import { resourceRules, resources } from "@server/db"; import { resourceRules, resourcePolicyRules, resources } from "@server/db";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
@@ -59,6 +59,48 @@ export async function deleteResourceRule(
const { ruleId } = parsedParams.data; const { ruleId } = parsedParams.data;
// Look up resource to determine which table to use
const { resourceId } = parsedParams.data;
const [resource] = await db
.select()
.from(resources)
.where(eq(resources.resourceId, resourceId))
.limit(1);
if (!resource) {
return next(
createHttpError(HttpCode.NOT_FOUND, "Resource not found")
);
}
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
if (isInlinePolicy) {
const [deletedRule] = await db
.delete(resourcePolicyRules)
.where(eq(resourcePolicyRules.ruleId, ruleId))
.returning();
if (!deletedRule) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Resource rule with ID ${ruleId} not found`
)
);
}
return response(res, {
data: null,
success: true,
error: false,
message: "Resource rule deleted successfully",
status: HttpCode.OK
});
}
// Delete the rule and return the deleted record // Delete the rule and return the deleted record
const [deletedRule] = await db const [deletedRule] = await db
.delete(resourceRules) .delete(resourceRules)
@@ -1,7 +1,11 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db } from "@server/db"; import { db } from "@server/db";
import { resourceWhitelist, users } from "@server/db"; // Assuming these are the correct tables import {
resourceWhitelist,
resourcePolicyWhiteList,
resources
} from "@server/db";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
@@ -23,6 +27,15 @@ async function queryWhitelist(resourceId: number) {
.where(eq(resourceWhitelist.resourceId, resourceId)); .where(eq(resourceWhitelist.resourceId, resourceId));
} }
async function queryPolicyWhitelist(policyId: number) {
return await db
.select({
email: resourcePolicyWhiteList.email
})
.from(resourcePolicyWhiteList)
.where(eq(resourcePolicyWhiteList.resourcePolicyId, policyId));
}
export type GetResourceWhitelistResponse = { export type GetResourceWhitelistResponse = {
whitelist: NonNullable<Awaited<ReturnType<typeof queryWhitelist>>>; whitelist: NonNullable<Awaited<ReturnType<typeof queryWhitelist>>>;
}; };
@@ -71,7 +84,25 @@ export async function getResourceWhitelist(
const { resourceId } = parsedParams.data; const { resourceId } = parsedParams.data;
const whitelist = await queryWhitelist(resourceId); const [resource] = await db
.select()
.from(resources)
.where(eq(resources.resourceId, resourceId))
.limit(1);
if (!resource) {
return next(
createHttpError(HttpCode.NOT_FOUND, "Resource not found")
);
}
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
const whitelist = isInlinePolicy
? await queryPolicyWhitelist(resource.defaultResourcePolicyId!)
: await queryWhitelist(resourceId);
return response<GetResourceWhitelistResponse>(res, { return response<GetResourceWhitelistResponse>(res, {
data: { data: {
+33 -2
View File
@@ -1,7 +1,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db } from "@server/db"; import { db } from "@server/db";
import { roleResources, roles } from "@server/db"; import { roleResources, roles, rolePolicies, resources } from "@server/db";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
@@ -27,6 +27,19 @@ async function query(resourceId: number) {
.where(eq(roleResources.resourceId, resourceId)); .where(eq(roleResources.resourceId, resourceId));
} }
async function queryInlinePolicy(policyId: number) {
return await db
.select({
roleId: roles.roleId,
name: roles.name,
description: roles.description,
isAdmin: roles.isAdmin
})
.from(rolePolicies)
.innerJoin(roles, eq(rolePolicies.roleId, roles.roleId))
.where(eq(rolePolicies.resourcePolicyId, policyId));
}
export type ListResourceRolesResponse = { export type ListResourceRolesResponse = {
roles: NonNullable<Awaited<ReturnType<typeof query>>>; roles: NonNullable<Awaited<ReturnType<typeof query>>>;
}; };
@@ -75,7 +88,25 @@ export async function listResourceRoles(
const { resourceId } = parsedParams.data; const { resourceId } = parsedParams.data;
const resourceRolesList = await query(resourceId); const [resource] = await db
.select()
.from(resources)
.where(eq(resources.resourceId, resourceId))
.limit(1);
if (!resource) {
return next(
createHttpError(HttpCode.NOT_FOUND, "Resource not found")
);
}
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
const resourceRolesList = isInlinePolicy
? await queryInlinePolicy(resource.defaultResourcePolicyId!)
: await query(resourceId);
return response<ListResourceRolesResponse>(res, { return response<ListResourceRolesResponse>(res, {
data: { data: {
+38 -5
View File
@@ -1,5 +1,5 @@
import { db } from "@server/db"; import { db } from "@server/db";
import { resourceRules, resources } from "@server/db"; import { resourceRules, resourcePolicyRules, resources } from "@server/db";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import response from "@server/lib/response"; import response from "@server/lib/response";
import { eq, sql } from "drizzle-orm"; import { eq, sql } from "drizzle-orm";
@@ -47,6 +47,21 @@ function queryResourceRules(resourceId: number) {
return baseQuery; return baseQuery;
} }
function queryPolicyRules(policyId: number) {
return db
.select({
ruleId: resourcePolicyRules.ruleId,
resourceId: sql<number | null>`null`,
action: resourcePolicyRules.action,
match: resourcePolicyRules.match,
value: resourcePolicyRules.value,
priority: resourcePolicyRules.priority,
enabled: resourcePolicyRules.enabled
})
.from(resourcePolicyRules)
.where(eq(resourcePolicyRules.resourcePolicyId, policyId));
}
export type ListResourceRulesResponse = { export type ListResourceRulesResponse = {
rules: Awaited<ReturnType<typeof queryResourceRules>>; rules: Awaited<ReturnType<typeof queryResourceRules>>;
pagination: { total: number; limit: number; offset: number }; pagination: { total: number; limit: number; offset: number };
@@ -125,16 +140,34 @@ export async function listResourceRules(
); );
} }
const baseQuery = queryResourceRules(resourceId); const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
let rulesList: Awaited<ReturnType<typeof queryResourceRules>>;
let totalCount: number;
if (isInlinePolicy) {
const policyId = resource.defaultResourcePolicyId!;
const policyRules = await queryPolicyRules(policyId)
.limit(limit)
.offset(offset);
const countResult = await db
.select({ count: sql<number>`cast(count(*) as integer)` })
.from(resourcePolicyRules)
.where(eq(resourcePolicyRules.resourcePolicyId, policyId));
rulesList = policyRules as typeof rulesList;
totalCount = countResult[0].count;
} else {
const baseQuery = queryResourceRules(resourceId);
const countQuery = db const countQuery = db
.select({ count: sql<number>`cast(count(*) as integer)` }) .select({ count: sql<number>`cast(count(*) as integer)` })
.from(resourceRules) .from(resourceRules)
.where(eq(resourceRules.resourceId, resourceId)); .where(eq(resourceRules.resourceId, resourceId));
rulesList = await baseQuery.limit(limit).offset(offset);
let rulesList = await baseQuery.limit(limit).offset(offset);
const totalCountResult = await countQuery; const totalCountResult = await countQuery;
const totalCount = totalCountResult[0].count; totalCount = totalCountResult[0].count;
}
// sort rules list by the priority in ascending order // sort rules list by the priority in ascending order
rulesList = rulesList.sort((a, b) => a.priority - b.priority); rulesList = rulesList.sort((a, b) => a.priority - b.priority);
@@ -1,7 +1,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db, resources } from "@server/db"; import { db, resources } from "@server/db";
import { roleResources, roles } from "@server/db"; import { roleResources, roles, rolePolicies } from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -130,6 +130,41 @@ export async function removeRoleFromResource(
); );
} }
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
if (isInlinePolicy) {
const policyId = resource.defaultResourcePolicyId!;
const existingEntry = await db
.select()
.from(rolePolicies)
.where(
and(
eq(rolePolicies.resourcePolicyId, policyId),
eq(rolePolicies.roleId, roleId)
)
);
if (existingEntry.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
"Role not found in resource"
)
);
}
await db
.delete(rolePolicies)
.where(
and(
eq(rolePolicies.resourcePolicyId, policyId),
eq(rolePolicies.roleId, roleId)
)
);
} else {
// Check if role exists in resource // Check if role exists in resource
const existingEntry = await db const existingEntry = await db
.select() .select()
@@ -158,6 +193,7 @@ export async function removeRoleFromResource(
eq(roleResources.roleId, roleId) eq(roleResources.roleId, roleId)
) )
); );
}
return response(res, { return response(res, {
data: {}, data: {},
@@ -1,7 +1,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db, resources } from "@server/db"; import { db, resources } from "@server/db";
import { userResources } from "@server/db"; import { userResources, userPolicies } from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -103,6 +103,41 @@ export async function removeUserFromResource(
); );
} }
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
if (isInlinePolicy) {
const policyId = resource.defaultResourcePolicyId!;
const existingEntry = await db
.select()
.from(userPolicies)
.where(
and(
eq(userPolicies.resourcePolicyId, policyId),
eq(userPolicies.userId, userId)
)
);
if (existingEntry.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
"User not found in resource"
)
);
}
await db
.delete(userPolicies)
.where(
and(
eq(userPolicies.resourcePolicyId, policyId),
eq(userPolicies.userId, userId)
)
);
} else {
// Check if user exists in resource // Check if user exists in resource
const existingEntry = await db const existingEntry = await db
.select() .select()
@@ -131,6 +166,7 @@ export async function removeUserFromResource(
eq(userResources.userId, userId) eq(userResources.userId, userId)
) )
); );
}
return response(res, { return response(res, {
data: {}, data: {},
@@ -3,7 +3,9 @@ import { z } from "zod";
import { import {
db, db,
resourceHeaderAuth, resourceHeaderAuth,
resourceHeaderAuthExtendedCompatibility resourceHeaderAuthExtendedCompatibility,
resourcePolicyHeaderAuth,
resources
} from "@server/db"; } from "@server/db";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
@@ -89,7 +91,43 @@ export async function setResourceHeaderAuth(
const { resourceId } = parsedParams.data; const { resourceId } = parsedParams.data;
const { user, password, extendedCompatibility } = parsedBody.data; const { user, password, extendedCompatibility } = parsedBody.data;
const [resource] = await db
.select()
.from(resources)
.where(eq(resources.resourceId, resourceId))
.limit(1);
if (!resource) {
return next(
createHttpError(HttpCode.NOT_FOUND, "Resource not found")
);
}
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
await db.transaction(async (trx) => { await db.transaction(async (trx) => {
if (isInlinePolicy) {
const policyId = resource.defaultResourcePolicyId!;
await trx
.delete(resourcePolicyHeaderAuth)
.where(
eq(resourcePolicyHeaderAuth.resourcePolicyId, policyId)
);
if (user && password && extendedCompatibility !== null) {
const headerAuthHash = await hashPassword(
Buffer.from(`${user}:${password}`).toString("base64")
);
await trx.insert(resourcePolicyHeaderAuth).values({
resourcePolicyId: policyId,
headerAuthHash,
extendedCompatibility: extendedCompatibility!
});
}
} else {
await trx await trx
.delete(resourceHeaderAuth) .delete(resourceHeaderAuth)
.where(eq(resourceHeaderAuth.resourceId, resourceId)); .where(eq(resourceHeaderAuth.resourceId, resourceId));
@@ -120,6 +158,7 @@ export async function setResourceHeaderAuth(
}) })
]); ]);
} }
}
}); });
return response(res, { return response(res, {
+37 -1
View File
@@ -1,7 +1,11 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db } from "@server/db"; import { db } from "@server/db";
import { resourcePassword } from "@server/db"; import {
resourcePassword,
resourcePolicyPassword,
resources
} from "@server/db";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -85,7 +89,38 @@ export async function setResourcePassword(
const { resourceId } = parsedParams.data; const { resourceId } = parsedParams.data;
const { password } = parsedBody.data; const { password } = parsedBody.data;
const [resource] = await db
.select()
.from(resources)
.where(eq(resources.resourceId, resourceId))
.limit(1);
if (!resource) {
return next(
createHttpError(HttpCode.NOT_FOUND, "Resource not found")
);
}
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
await db.transaction(async (trx) => { await db.transaction(async (trx) => {
if (isInlinePolicy) {
const policyId = resource.defaultResourcePolicyId!;
await trx
.delete(resourcePolicyPassword)
.where(
eq(resourcePolicyPassword.resourcePolicyId, policyId)
);
if (password) {
const passwordHash = await hashPassword(password);
await trx
.insert(resourcePolicyPassword)
.values({ resourcePolicyId: policyId, passwordHash });
}
} else {
await trx await trx
.delete(resourcePassword) .delete(resourcePassword)
.where(eq(resourcePassword.resourceId, resourceId)); .where(eq(resourcePassword.resourceId, resourceId));
@@ -97,6 +132,7 @@ export async function setResourcePassword(
.insert(resourcePassword) .insert(resourcePassword)
.values({ resourceId, passwordHash }); .values({ resourceId, passwordHash });
} }
}
}); });
return response(res, { return response(res, {
+35 -1
View File
@@ -1,7 +1,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db } from "@server/db"; import { db } from "@server/db";
import { resourcePincode } from "@server/db"; import { resourcePincode, resourcePolicyPincode, resources } from "@server/db";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -89,7 +89,40 @@ export async function setResourcePincode(
const { resourceId } = parsedParams.data; const { resourceId } = parsedParams.data;
const { pincode } = parsedBody.data; const { pincode } = parsedBody.data;
const [resource] = await db
.select()
.from(resources)
.where(eq(resources.resourceId, resourceId))
.limit(1);
if (!resource) {
return next(
createHttpError(HttpCode.NOT_FOUND, "Resource not found")
);
}
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
await db.transaction(async (trx) => { await db.transaction(async (trx) => {
if (isInlinePolicy) {
const policyId = resource.defaultResourcePolicyId!;
await trx
.delete(resourcePolicyPincode)
.where(
eq(resourcePolicyPincode.resourcePolicyId, policyId)
);
if (pincode) {
const pincodeHash = await hashPassword(pincode);
await trx.insert(resourcePolicyPincode).values({
resourcePolicyId: policyId,
pincodeHash,
digitLength: 6
});
}
} else {
await trx await trx
.delete(resourcePincode) .delete(resourcePincode)
.where(eq(resourcePincode.resourceId, resourceId)); .where(eq(resourcePincode.resourceId, resourceId));
@@ -101,6 +134,7 @@ export async function setResourcePincode(
.insert(resourcePincode) .insert(resourcePincode)
.values({ resourceId, pincodeHash, digitLength: 6 }); .values({ resourceId, pincodeHash, digitLength: 6 });
} }
}
}); });
return response(res, { return response(res, {
+35 -2
View File
@@ -1,7 +1,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db, resources } from "@server/db"; import { db, resources } from "@server/db";
import { apiKeys, roleResources, roles } from "@server/db"; import { apiKeys, roleResources, roles, rolePolicies } from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -129,7 +129,39 @@ export async function setResourceRoles(
); );
const adminRoleIds = adminRoles.map((role) => role.roleId); const adminRoleIds = adminRoles.map((role) => role.roleId);
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
await db.transaction(async (trx) => { await db.transaction(async (trx) => {
if (isInlinePolicy) {
const policyId = resource.defaultResourcePolicyId!;
// For inline policy, preserve admin roles by only deleting non-admin entries
if (adminRoleIds.length > 0) {
await trx
.delete(rolePolicies)
.where(
and(
eq(rolePolicies.resourcePolicyId, policyId),
ne(rolePolicies.roleId, adminRoleIds[0])
)
);
} else {
await trx
.delete(rolePolicies)
.where(eq(rolePolicies.resourcePolicyId, policyId));
}
await Promise.all(
roleIds.map((roleId) =>
trx
.insert(rolePolicies)
.values({ roleId, resourcePolicyId: policyId })
.returning()
)
);
} else {
if (adminRoleIds.length > 0) { if (adminRoleIds.length > 0) {
await trx.delete(roleResources).where( await trx.delete(roleResources).where(
and( and(
@@ -143,7 +175,7 @@ export async function setResourceRoles(
.where(eq(roleResources.resourceId, resourceId)); .where(eq(roleResources.resourceId, resourceId));
} }
const newRoleResources = await Promise.all( await Promise.all(
roleIds.map((roleId) => roleIds.map((roleId) =>
trx trx
.insert(roleResources) .insert(roleResources)
@@ -151,6 +183,7 @@ export async function setResourceRoles(
.returning() .returning()
) )
); );
}
return response(res, { return response(res, {
data: {}, data: {},
+34 -2
View File
@@ -1,7 +1,7 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db } from "@server/db"; import { db } from "@server/db";
import { userResources } from "@server/db"; import { userResources, userPolicies, resources } from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -82,12 +82,43 @@ export async function setResourceUsers(
const { resourceId } = parsedParams.data; const { resourceId } = parsedParams.data;
const [resource] = await db
.select()
.from(resources)
.where(eq(resources.resourceId, resourceId))
.limit(1);
if (!resource) {
return next(
createHttpError(HttpCode.NOT_FOUND, "Resource not found")
);
}
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
await db.transaction(async (trx) => { await db.transaction(async (trx) => {
if (isInlinePolicy) {
const policyId = resource.defaultResourcePolicyId!;
await trx
.delete(userPolicies)
.where(eq(userPolicies.resourcePolicyId, policyId));
await Promise.all(
userIds.map((userId) =>
trx
.insert(userPolicies)
.values({ userId, resourcePolicyId: policyId })
.returning()
)
);
} else {
await trx await trx
.delete(userResources) .delete(userResources)
.where(eq(userResources.resourceId, resourceId)); .where(eq(userResources.resourceId, resourceId));
const newUserResources = await Promise.all( await Promise.all(
userIds.map((userId) => userIds.map((userId) =>
trx trx
.insert(userResources) .insert(userResources)
@@ -95,6 +126,7 @@ export async function setResourceUsers(
.returning() .returning()
) )
); );
}
return response(res, { return response(res, {
data: {}, data: {},
@@ -1,7 +1,12 @@
import { Request, Response, NextFunction } from "express"; import { Request, Response, NextFunction } from "express";
import { z } from "zod"; import { z } from "zod";
import { db } from "@server/db"; import { db } from "@server/db";
import { resources, resourceWhitelist } from "@server/db"; import {
resources,
resourceWhitelist,
resourcePolicies,
resourcePolicyWhiteList
} from "@server/db";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -104,6 +109,83 @@ export async function setResourceWhitelist(
); );
} }
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
if (isInlinePolicy) {
const policyId = resource.defaultResourcePolicyId!;
const [policy] = await db
.select()
.from(resourcePolicies)
.where(eq(resourcePolicies.resourcePolicyId, policyId));
if (!policy) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
"Resource policy not found"
)
);
}
if (!policy.emailWhitelistEnabled) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Email whitelist is not enabled for this resource"
)
);
}
const existingPolicyWhitelist = await db
.select()
.from(resourcePolicyWhiteList)
.where(eq(resourcePolicyWhiteList.resourcePolicyId, policyId));
await db.transaction(async (trx) => {
const existingEmails = existingPolicyWhitelist.map(
(w) => w.email
);
const emailsToAdd = emails.filter(
(e) => !existingEmails.includes(e)
);
const emailsToRemove = existingEmails.filter(
(e) => !emails.includes(e)
);
for (const email of emailsToAdd) {
await trx.insert(resourcePolicyWhiteList).values({
email,
resourcePolicyId: policyId
});
}
for (const email of emailsToRemove) {
await trx
.delete(resourcePolicyWhiteList)
.where(
and(
eq(
resourcePolicyWhiteList.resourcePolicyId,
policyId
),
eq(resourcePolicyWhiteList.email, email)
)
);
}
return response(res, {
data: {},
success: true,
error: false,
message: "Whitelist set for resource successfully",
status: HttpCode.CREATED
});
});
} else {
if (!resource.emailWhitelistEnabled) { if (!resource.emailWhitelistEnabled) {
return next( return next(
createHttpError( createHttpError(
@@ -155,6 +237,7 @@ export async function setResourceWhitelist(
status: HttpCode.CREATED status: HttpCode.CREATED
}); });
}); });
}
} catch (error) { } catch (error) {
logger.error(error); logger.error(error);
return next( return next(
+52
View File
@@ -549,6 +549,58 @@ async function updateHttpResource(
updateData.maintenanceEstimatedTime = undefined; updateData.maintenanceEstimatedTime = undefined;
} }
const isInlinePolicy =
resource.resourcePolicyId === null &&
resource.defaultResourcePolicyId !== null;
if (isInlinePolicy) {
const policyId = resource.defaultResourcePolicyId!;
const {
sso,
emailWhitelistEnabled,
applyRules,
skipToIdpId,
...resourceOnlyData
} = updateData;
const policyUpdate: Record<string, unknown> = {};
if (sso !== undefined) policyUpdate.sso = sso;
if (emailWhitelistEnabled !== undefined)
policyUpdate.emailWhitelistEnabled = emailWhitelistEnabled;
if (applyRules !== undefined) policyUpdate.applyRules = applyRules;
if (skipToIdpId !== undefined) policyUpdate.idpId = skipToIdpId;
if (Object.keys(policyUpdate).length > 0) {
await db
.update(resourcePolicies)
.set(policyUpdate)
.where(eq(resourcePolicies.resourcePolicyId, policyId));
}
const updatedResource = await db
.update(resources)
.set({ ...resourceOnlyData, headers })
.where(eq(resources.resourceId, resource.resourceId))
.returning();
if (updatedResource.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Resource with ID ${resource.resourceId} not found`
)
);
}
return response(res, {
data: updatedResource[0],
success: true,
error: false,
message: "HTTP resource updated successfully",
status: HttpCode.OK
});
}
const updatedResource = await db const updatedResource = await db
.update(resources) .update(resources)
.set({ ...updateData, headers }) .set({ ...updateData, headers })