diff --git a/server/lib/blueprints/clientResources.ts b/server/lib/blueprints/clientResources.ts index 1b2ec2ef7..9049bc3be 100644 --- a/server/lib/blueprints/clientResources.ts +++ b/server/lib/blueprints/clientResources.ts @@ -3,6 +3,7 @@ import { clientSiteResources, domains, orgDomains, + roleActions, roles, roleSiteResources, Site, @@ -19,6 +20,7 @@ import { sites } from "@server/db"; import { eq, and, ne, inArray, or, isNotNull } from "drizzle-orm"; import { Config } from "./types"; import logger from "@server/logger"; +import { defaultRoleAllowedActions } from "@server/routers/role/createRole"; import { getNextAvailableAliasAddress } from "../ip"; import { createCertificate } from "#dynamic/routers/certificates/createCertificate"; @@ -335,8 +337,7 @@ export async function updateClientResources( } if (resourceData.roles.length > 0) { - // Re-add specified roles but we need to get the roleIds from the role name in the array - const rolesToUpdate = await trx + const existingRoles = await trx .select() .from(roles) .where( @@ -346,7 +347,28 @@ export async function updateClientResources( ) ); - const roleIds = rolesToUpdate.map((role) => role.roleId); + const foundNames = new Set(existingRoles.map((r) => r.name)); + const missingNames = resourceData.roles.filter( + (n) => !foundNames.has(n) + ); + + for (const name of missingNames) { + const [created] = await trx + .insert(roles) + .values({ name, orgId }) + .returning(); + await trx.insert(roleActions).values( + defaultRoleAllowedActions.map((action) => ({ + roleId: created.roleId, + actionId: action, + orgId + })) + ); + existingRoles.push(created); + logger.info(`Auto-created role "${name}" in org ${orgId} from blueprint`); + } + + const roleIds = existingRoles.map((role) => role.roleId); await trx .insert(roleSiteResources) @@ -447,8 +469,7 @@ export async function updateClientResources( }); if (resourceData.roles.length > 0) { - // get roleIds from role names - const rolesToUpdate = await trx + const existingRoles = await trx .select() .from(roles) .where( @@ -458,7 +479,28 @@ export async function updateClientResources( ) ); - const roleIds = rolesToUpdate.map((role) => role.roleId); + const foundNames = new Set(existingRoles.map((r) => r.name)); + const missingNames = resourceData.roles.filter( + (n) => !foundNames.has(n) + ); + + for (const name of missingNames) { + const [created] = await trx + .insert(roles) + .values({ name, orgId }) + .returning(); + await trx.insert(roleActions).values( + defaultRoleAllowedActions.map((action) => ({ + roleId: created.roleId, + actionId: action, + orgId + })) + ); + existingRoles.push(created); + logger.info(`Auto-created role "${name}" in org ${orgId} from blueprint`); + } + + const roleIds = existingRoles.map((role) => role.roleId); await trx .insert(roleSiteResources) diff --git a/server/lib/blueprints/proxyResources.ts b/server/lib/blueprints/proxyResources.ts index ba93bc46a..e98af003e 100644 --- a/server/lib/blueprints/proxyResources.ts +++ b/server/lib/blueprints/proxyResources.ts @@ -8,6 +8,7 @@ import { resourcePincode, resourceRules, resourceWhitelist, + roleActions, roleResources, roles, Target, @@ -36,6 +37,7 @@ import { isValidRegionId } from "@server/db/regions"; import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; import { fireHealthCheckUnknownAlert } from "#dynamic/lib/alerts"; import { tierMatrix } from "../billing/tierMatrix"; +import { defaultRoleAllowedActions } from "@server/routers/role/createRole"; export type ProxyResourcesResults = { proxyResource: Resource; @@ -922,14 +924,26 @@ async function syncRoleResources( .where(eq(roleResources.resourceId, resourceId)); for (const roleName of ssoRoles) { - const [role] = await trx + let [role] = await trx .select() .from(roles) .where(and(eq(roles.name, roleName), eq(roles.orgId, orgId))) .limit(1); if (!role) { - throw new Error(`Role not found: ${roleName} in org ${orgId}`); + const [created] = await trx + .insert(roles) + .values({ name: roleName, orgId }) + .returning(); + await trx.insert(roleActions).values( + defaultRoleAllowedActions.map((action) => ({ + roleId: created.roleId, + actionId: action, + orgId + })) + ); + role = created; + logger.info(`Auto-created role "${roleName}" in org ${orgId} from blueprint`); } if (role.isAdmin) {