From 545ea4e27c28cc0dc98bf3340b532ca93ed81934 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 19 Oct 2025 15:23:21 +0530 Subject: [PATCH] feat: switched to root org id pattern --- backend/src/@types/fastify.d.ts | 1 + .../db/migrations/20251018061215_sub-org.ts | 10 +++++-- backend/src/db/schemas/organizations.ts | 3 ++- .../src/ee/services/group/group-service.ts | 2 +- .../ee/services/permission/permission-dal.ts | 6 ++--- .../services/permission/permission-service.ts | 4 +-- .../project-template-types.ts | 14 +++++----- .../ee/services/sub-org/sub-org-service.ts | 11 +++++--- backend/src/lib/types/index.ts | 1 + .../server/plugins/auth/inject-identity.ts | 27 ++++++++++++++----- .../server/plugins/auth/inject-permission.ts | 8 ++++-- .../server/routes/v1/organization-router.ts | 2 +- .../services/auth-token/auth-token-service.ts | 9 ++++--- .../src/services/auth/auth-signup-service.ts | 8 +++++- .../identity-access-token-service.ts | 14 ++++++---- .../membership-user/membership-user-dal.ts | 8 ++++++ backend/src/services/org/org-dal.ts | 14 +++++----- backend/src/services/org/org-service.ts | 8 +++--- .../src/services/project/project-service.ts | 1 - .../service-token/service-token-service.ts | 15 +++++++++-- 20 files changed, 114 insertions(+), 52 deletions(-) diff --git a/backend/src/@types/fastify.d.ts b/backend/src/@types/fastify.d.ts index 1a28a6879..5480f6dde 100644 --- a/backend/src/@types/fastify.d.ts +++ b/backend/src/@types/fastify.d.ts @@ -180,6 +180,7 @@ declare module "fastify" { id: string; orgId: string; parentOrgId: string; + rootOrgId: string; }; rateLimits: RateLimitConfiguration; // passport data diff --git a/backend/src/db/migrations/20251018061215_sub-org.ts b/backend/src/db/migrations/20251018061215_sub-org.ts index 0b2f72abf..ec14e5fc5 100644 --- a/backend/src/db/migrations/20251018061215_sub-org.ts +++ b/backend/src/db/migrations/20251018061215_sub-org.ts @@ -6,8 +6,12 @@ export async function up(knex: Knex): Promise { const hasParentOrgId = await knex.schema.hasColumn(TableName.Organization, "parentOrgId"); if (!hasParentOrgId) { await knex.schema.alterTable(TableName.Organization, (t) => { + // the one just above the chain t.uuid("parentOrgId"); t.foreign("parentOrgId").references("id").inTable(TableName.Organization).onDelete("CASCADE"); + // this would root organization containing various informations like billing etc + t.uuid("rootOrgId"); + t.foreign("rootOrgId").references("id").inTable(TableName.Organization).onDelete("CASCADE"); }); } @@ -22,9 +26,11 @@ export async function up(knex: Knex): Promise { export async function down(knex: Knex): Promise { const hasParentOrgId = await knex.schema.hasColumn(TableName.Organization, "parentOrgId"); - if (hasParentOrgId) { + const hasRootOrgId = await knex.schema.hasColumn(TableName.Organization, "rootOrgId"); + if (hasParentOrgId || hasRootOrgId) { await knex.schema.alterTable(TableName.Organization, (t) => { - t.dropColumn("parentOrgId"); + if (hasParentOrgId) t.dropColumn("parentOrgId"); + if (hasRootOrgId) t.dropColumn("rootOrgId"); }); } diff --git a/backend/src/db/schemas/organizations.ts b/backend/src/db/schemas/organizations.ts index 38c6f797d..a1c01151f 100644 --- a/backend/src/db/schemas/organizations.ts +++ b/backend/src/db/schemas/organizations.ts @@ -39,7 +39,8 @@ export const OrganizationsSchema = z.object({ maxSharedSecretViewLimit: z.number().nullable().optional(), googleSsoAuthEnforced: z.boolean().default(false), googleSsoAuthLastUsed: z.date().nullable().optional(), - parentOrgId: z.string().uuid().nullable().optional() + parentOrgId: z.string().uuid().nullable().optional(), + rootOrgId: z.string().uuid().nullable().optional() }); export type TOrganizations = z.infer; diff --git a/backend/src/ee/services/group/group-service.ts b/backend/src/ee/services/group/group-service.ts index 0ffd77f0d..956d7853a 100644 --- a/backend/src/ee/services/group/group-service.ts +++ b/backend/src/ee/services/group/group-service.ts @@ -460,7 +460,7 @@ export const groupServiceFactory = ({ const { permission } = await permissionService.getOrgPermission({ actor, actorId, - actorOrgId, + orgId: actorOrgId, actorAuthMethod, actorOrgId, scope: OrganizationActionScope.Any diff --git a/backend/src/ee/services/permission/permission-dal.ts b/backend/src/ee/services/permission/permission-dal.ts index d35abe665..95480a54a 100644 --- a/backend/src/ee/services/permission/permission-dal.ts +++ b/backend/src/ee/services/permission/permission-dal.ts @@ -19,7 +19,7 @@ interface TPermissionDataReturn extends TMemberships { orgAuthEnforced?: boolean | null; orgGoogleSsoAuthEnforced?: boolean | null; shouldUseNewPrivilegeSystem?: boolean | null; - parentOrgId?: boolean | null; + rootOrgId?: string | null; bypassOrgAuthEnabled?: boolean | null; roles: { id: string; @@ -275,7 +275,7 @@ export const permissionDALFactory = (db: TDbClient): TPermissionDALFactory => { db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"), db.ref("googleSsoAuthEnforced").withSchema(TableName.Organization).as("orgGoogleSsoAuthEnforced"), db.ref("bypassOrgAuthEnabled").withSchema(TableName.Organization).as("bypassOrgAuthEnabled"), - db.ref("parentOrgId").withSchema(TableName.Organization).as("parentOrgId") + db.ref("rootOrgId").withSchema(TableName.Organization).as("rootOrgId") ); const data = sqlNestRelationships({ @@ -285,7 +285,7 @@ export const permissionDALFactory = (db: TDbClient): TPermissionDALFactory => { MembershipsSchema.extend({ orgAuthEnforced: z.boolean().optional().nullable(), shouldUseNewPrivilegeSystem: z.boolean().optional().nullable(), - parentOrgId: z.string().optional().nullable(), + rootOrgId: z.string().optional().nullable(), orgGoogleSsoAuthEnforced: z.boolean(), bypassOrgAuthEnabled: z.boolean() }).parse(el), diff --git a/backend/src/ee/services/permission/permission-service.ts b/backend/src/ee/services/permission/permission-service.ts index dc4874b10..ec2a21352 100644 --- a/backend/src/ee/services/permission/permission-service.ts +++ b/backend/src/ee/services/permission/permission-service.ts @@ -209,8 +209,8 @@ export const permissionServiceFactory = ({ }); if (!permissionData?.length) throw new ForbiddenRequestError({ name: "You are not member of this organization" }); - const parentOrgId = permissionData?.[0]?.parentOrgId; - const isChild = Boolean(parentOrgId); + const rootOrgId = permissionData?.[0]?.rootOrgId; + const isChild = Boolean(rootOrgId); if (scope === OrganizationActionScope.ParentOrganization && isChild) { throw new BadRequestError({ message: `Child organization cannot do this operation` }); } else if (scope === OrganizationActionScope.ChildOrganization && !isChild) { diff --git a/backend/src/ee/services/project-template/project-template-types.ts b/backend/src/ee/services/project-template/project-template-types.ts index 8d9e952a7..1815344a7 100644 --- a/backend/src/ee/services/project-template/project-template-types.ts +++ b/backend/src/ee/services/project-template/project-template-types.ts @@ -2,7 +2,7 @@ import { z } from "zod"; import { ProjectMembershipRole, ProjectType, TProjectEnvironments } from "@app/db/schemas"; import { TProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission"; -import { OrgServiceActor } from "@app/lib/types"; +import { ProjectServiceActor } from "@app/lib/types"; import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission"; export type TProjectTemplateEnvironment = Pick; @@ -31,7 +31,7 @@ export enum InfisicalProjectTemplate { export type TProjectTemplateServiceFactory = { listProjectTemplatesByOrg: ( - actor: OrgServiceActor, + actor: ProjectServiceActor, type?: ProjectType ) => Promise< ( @@ -85,7 +85,7 @@ export type TProjectTemplateServiceFactory = { >; createProjectTemplate: ( arg: TCreateProjectTemplateDTO, - actor: OrgServiceActor + actor: ProjectServiceActor ) => Promise<{ environments: TProjectTemplateEnvironment[]; roles: { @@ -109,7 +109,7 @@ export type TProjectTemplateServiceFactory = { updateProjectTemplateById: ( id: string, { roles, environments, ...params }: TUpdateProjectTemplateDTO, - actor: OrgServiceActor + actor: ProjectServiceActor ) => Promise<{ environments: TProjectTemplateEnvironment[]; roles: { @@ -132,7 +132,7 @@ export type TProjectTemplateServiceFactory = { }>; deleteProjectTemplateById: ( id: string, - actor: OrgServiceActor + actor: ProjectServiceActor ) => Promise<{ environments: TProjectTemplateEnvironment[]; roles: { @@ -155,7 +155,7 @@ export type TProjectTemplateServiceFactory = { }>; findProjectTemplateById: ( id: string, - actor: OrgServiceActor + actor: ProjectServiceActor ) => Promise<{ packedRoles: TProjectTemplateRole[]; environments: TProjectTemplateEnvironment[]; @@ -179,7 +179,7 @@ export type TProjectTemplateServiceFactory = { }>; findProjectTemplateByName: ( name: string, - actor: OrgServiceActor + actor: ProjectServiceActor ) => Promise<{ packedRoles: TProjectTemplateRole[]; environments: TProjectTemplateEnvironment[]; diff --git a/backend/src/ee/services/sub-org/sub-org-service.ts b/backend/src/ee/services/sub-org/sub-org-service.ts index 86dd03abd..8bb6da13d 100644 --- a/backend/src/ee/services/sub-org/sub-org-service.ts +++ b/backend/src/ee/services/sub-org/sub-org-service.ts @@ -44,7 +44,7 @@ export const subOrgServiceFactory = ({ OrgPermissionSubjects.ChildOrganization ); - const orgLicensePlan = await licenseService.getPlan(permissionActor.parentOrgId); + const orgLicensePlan = await licenseService.getPlan(permissionActor.rootOrgId); if (!orgLicensePlan.subOrganization) { throw new BadRequestError({ message: "Child organization creation failed. Please upgrade your instance to Infisical's Enterprise plan." @@ -52,7 +52,10 @@ export const subOrgServiceFactory = ({ } const organization = await orgDAL.transaction(async (tx) => { - const org = await orgDAL.create({ name, slug: name, parentOrgId: permissionActor.orgId }, tx); + const org = await orgDAL.create( + { name, slug: name, rootOrgId: permissionActor.orgId, parentOrgId: permissionActor.orgId }, + tx + ); const membership = await membershipDAL.create( { scope: AccessScope.Organization, @@ -83,7 +86,7 @@ export const subOrgServiceFactory = ({ actorId: permissionActor.id, actor: permissionActor.type, orgId: permissionActor.parentOrgId, - actorOrgId: permissionActor.parentOrgId, + actorOrgId: permissionActor.rootOrgId, actorAuthMethod: permissionActor.authMethod, scope: OrganizationActionScope.ParentOrganization }); @@ -91,7 +94,7 @@ export const subOrgServiceFactory = ({ const organizations = await orgDAL.listSubOrganizations({ actorId: permissionActor.id, actorType: permissionActor.type, - orgId: permissionActor.parentOrgId, + orgId: permissionActor.rootOrgId, isAccessible: data?.isAccessible, limit: data?.limit, offset: data?.offset diff --git a/backend/src/lib/types/index.ts b/backend/src/lib/types/index.ts index ff6013b7f..f29de20f3 100644 --- a/backend/src/lib/types/index.ts +++ b/backend/src/lib/types/index.ts @@ -78,6 +78,7 @@ export type OrgServiceActor = { id: string; authMethod: ActorAuthMethod; orgId: string; + rootOrgId: string; parentOrgId: string; }; diff --git a/backend/src/server/plugins/auth/inject-identity.ts b/backend/src/server/plugins/auth/inject-identity.ts index f91d56f32..bde9be050 100644 --- a/backend/src/server/plugins/auth/inject-identity.ts +++ b/backend/src/server/plugins/auth/inject-identity.ts @@ -20,6 +20,7 @@ export type TAuthMode = tokenVersionId: string; // the session id of token used user: TUsers; orgId: string; + rootOrgId: string; parentOrgId: string; authMethod: AuthMethod; isMfaVerified?: boolean; @@ -32,6 +33,7 @@ export type TAuthMode = userId: string; user: TUsers; orgId: string; + rootOrgId: string; parentOrgId: string; token: string; } @@ -41,6 +43,7 @@ export type TAuthMode = actor: ActorType.SERVICE; serviceTokenId: string; orgId: string; + rootOrgId: string; parentOrgId: string; authMethod: null; token: string; @@ -51,6 +54,7 @@ export type TAuthMode = identityId: string; identityName: string; orgId: string; + rootOrgId: string; parentOrgId: string; authMethod: null; isInstanceAdmin?: boolean; @@ -61,6 +65,7 @@ export type TAuthMode = actor: ActorType.SCIM_CLIENT; scimTokenId: string; orgId: string; + rootOrgId: string; parentOrgId: string; authMethod: null; }; @@ -145,10 +150,8 @@ export const injectIdentity = fp( switch (authMode) { case AuthMode.JWT: { - const { user, tokenVersionId, orgId, parentOrgId } = await server.services.authToken.fnValidateJwtIdentity( - token, - subOrganizationSelector - ); + const { user, tokenVersionId, orgId, rootOrgId, parentOrgId } = + await server.services.authToken.fnValidateJwtIdentity(token, subOrganizationSelector); requestContext.set("orgId", orgId); req.auth = { @@ -158,6 +161,7 @@ export const injectIdentity = fp( tokenVersionId, actor, orgId, + rootOrgId, parentOrgId, authMethod: token.authMethod, isMfaVerified: token.isMfaVerified, @@ -177,6 +181,7 @@ export const injectIdentity = fp( authMode: AuthMode.IDENTITY_ACCESS_TOKEN, actor, orgId: identity.orgId, + rootOrgId: identity.rootOrgId, parentOrgId: identity.parentOrgId, identityId: identity.identityId, identityName: identity.name, @@ -213,7 +218,8 @@ export const injectIdentity = fp( req.auth = { orgId: serviceToken.orgId, - parentOrgId: serviceToken.orgId, + rootOrgId: serviceToken.rootOrgId, + parentOrgId: serviceToken.parentOrgId, authMode: AuthMode.SERVICE_TOKEN as const, serviceToken, serviceTokenId: serviceToken.id, @@ -235,7 +241,16 @@ export const injectIdentity = fp( if (subOrganizationSelector) throw new BadRequestError({ message: `Service token doesn't support sub organization selector` }); - req.auth = { authMode: AuthMode.SCIM_TOKEN, actor, scimTokenId, orgId, authMethod: null, parentOrgId: orgId }; + req.auth = { + authMode: AuthMode.SCIM_TOKEN, + actor, + scimTokenId, + orgId, + authMethod: null, + // scim cannot be done for sub organization + rootOrgId: orgId, + parentOrgId: orgId + }; break; } default: diff --git a/backend/src/server/plugins/auth/inject-permission.ts b/backend/src/server/plugins/auth/inject-permission.ts index da5e7e54e..827a055d3 100644 --- a/backend/src/server/plugins/auth/inject-permission.ts +++ b/backend/src/server/plugins/auth/inject-permission.ts @@ -15,6 +15,7 @@ export const injectPermission = fp(async (server) => { id: req.auth.userId, orgId: req.auth.orgId, // if the req.auth.authMode is AuthMode.API_KEY, the orgId will be "API_KEY" authMethod: req.auth.authMethod, // if the req.auth.authMode is AuthMode.API_KEY, the authMethod will be null + rootOrgId: req.auth.rootOrgId, parentOrgId: req.auth.parentOrgId }; @@ -27,6 +28,7 @@ export const injectPermission = fp(async (server) => { id: req.auth.identityId, orgId: req.auth.orgId, authMethod: null, + rootOrgId: req.auth.rootOrgId, parentOrgId: req.auth.parentOrgId }; @@ -38,7 +40,8 @@ export const injectPermission = fp(async (server) => { type: ActorType.SERVICE, id: req.auth.serviceTokenId, orgId: req.auth.orgId, - parentOrgId: req.auth.orgId, + rootOrgId: req.auth.rootOrgId, + parentOrgId: req.auth.parentOrgId, authMethod: null }; @@ -50,7 +53,8 @@ export const injectPermission = fp(async (server) => { type: ActorType.SCIM_CLIENT, id: req.auth.scimTokenId, orgId: req.auth.orgId, - parentOrgId: req.auth.orgId, + rootOrgId: req.auth.rootOrgId, + parentOrgId: req.auth.parentOrgId, authMethod: null }; diff --git a/backend/src/server/routes/v1/organization-router.ts b/backend/src/server/routes/v1/organization-router.ts index b640fba1a..81165f1e7 100644 --- a/backend/src/server/routes/v1/organization-router.ts +++ b/backend/src/server/routes/v1/organization-router.ts @@ -76,7 +76,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { req.permission.id, req.params.organizationId, req.permission.authMethod, - req.permission.parentOrgId, + req.permission.rootOrgId, req.permission.orgId ); return { organization }; diff --git a/backend/src/services/auth-token/auth-token-service.ts b/backend/src/services/auth-token/auth-token-service.ts index 6d0dfcd4c..984fb4c31 100644 --- a/backend/src/services/auth-token/auth-token-service.ts +++ b/backend/src/services/auth-token/auth-token-service.ts @@ -210,11 +210,12 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL, membershipUserDAL, orgD if (!user || !user.isAccepted) throw new NotFoundError({ message: `User with ID '${session.userId}' not found` }); let orgId = ""; + let rootOrgId = ""; let parentOrgId = ""; if (token.organizationId) { if (subOrganizationSelector) { const subOrganization = await orgDAL.findOne({ - parentOrgId: token.organizationId, + rootOrgId: token.organizationId, slug: subOrganizationSelector }); if (!subOrganization) @@ -234,7 +235,8 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL, membershipUserDAL, orgD throw new ForbiddenRequestError({ message: "User organization membership is inactive" }); } orgId = subOrganization.id; - parentOrgId = token.organizationId; + rootOrgId = token.organizationId; + parentOrgId = subOrganization.parentOrgId; } else { const orgMembership = await membershipUserDAL.findOne({ actorUserId: user.id, @@ -251,11 +253,12 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL, membershipUserDAL, orgD } orgId = token.organizationId; + rootOrgId = token.organizationId; parentOrgId = token.organizationId; } } - return { user, tokenVersionId: token.tokenVersionId, orgId, parentOrgId }; + return { user, tokenVersionId: token.tokenVersionId, orgId, rootOrgId, parentOrgId }; }; return { diff --git a/backend/src/services/auth/auth-signup-service.ts b/backend/src/services/auth/auth-signup-service.ts index a2e426a2e..14f4387b9 100644 --- a/backend/src/services/auth/auth-signup-service.ts +++ b/backend/src/services/auth/auth-signup-service.ts @@ -258,7 +258,13 @@ export const authSignupServiceFactory = ({ let refreshTokenExpiresIn: string | number = appCfg.JWT_REFRESH_LIFETIME; if (organizationId) { - const org = await orgService.findOrganizationById(user.id, organizationId, authMethod, organizationId); + const org = await orgService.findOrganizationById( + user.id, + organizationId, + authMethod, + organizationId, + organizationId + ); if (org && org.userTokenExpiration) { tokenSessionExpiresIn = getMinExpiresIn(appCfg.JWT_AUTH_LIFETIME, org.userTokenExpiration); refreshTokenExpiresIn = org.userTokenExpiration; diff --git a/backend/src/services/identity-access-token/identity-access-token-service.ts b/backend/src/services/identity-access-token/identity-access-token-service.ts index f565ebf65..bbefe923c 100644 --- a/backend/src/services/identity-access-token/identity-access-token-service.ts +++ b/backend/src/services/identity-access-token/identity-access-token-service.ts @@ -210,10 +210,12 @@ export const identityAccessTokenServiceFactory = ({ }); } let orgId = ""; - const parentOrgId = identityAccessToken.identityScopeOrgId; + let parentOrgId = ""; + const identityOrgDetails = await orgDAL.findOne({ id: identityAccessToken.identityScopeOrgId }); + const rootOrgId = identityOrgDetails.rootOrgId || identityOrgDetails.id; if (subOrganizationSelector) { - const subOrganization = await orgDAL.findOne({ parentOrgId, slug: subOrganizationSelector }); + const subOrganization = await orgDAL.findOne({ rootOrgId, slug: subOrganizationSelector }); if (!subOrganizationSelector) throw new BadRequestError({ message: `Sub organization ${subOrganizationSelector} not found` }); @@ -227,18 +229,20 @@ export const identityAccessTokenServiceFactory = ({ throw new BadRequestError({ message: "Identity does not belong to any organization" }); } orgId = subOrganization.id; + parentOrgId = subOrganization.parentOrgId as string; } else { const identityOrgMembership = await membershipIdentityDAL.findOne({ scope: AccessScope.Organization, actorIdentityId: identityAccessToken.identityId, - scopeOrgId: parentOrgId + scopeOrgId: rootOrgId }); if (!identityOrgMembership) { throw new BadRequestError({ message: "Identity does not belong to any organization" }); } - orgId = parentOrgId; + orgId = rootOrgId; + parentOrgId = rootOrgId; } let { accessTokenNumUses } = identityAccessToken; @@ -249,7 +253,7 @@ export const identityAccessTokenServiceFactory = ({ await validateAccessTokenExp({ ...identityAccessToken, accessTokenNumUses }); await accessTokenQueue.updateIdentityAccessTokenStatus(identityAccessToken.id, Number(accessTokenNumUses) + 1); - return { ...identityAccessToken, orgId, parentOrgId }; + return { ...identityAccessToken, orgId, rootOrgId, parentOrgId }; }; return { renewAccessToken, revokeAccessToken, fnValidateIdentityAccessToken }; diff --git a/backend/src/services/membership-user/membership-user-dal.ts b/backend/src/services/membership-user/membership-user-dal.ts index 7882b9639..69b9585ed 100644 --- a/backend/src/services/membership-user/membership-user-dal.ts +++ b/backend/src/services/membership-user/membership-user-dal.ts @@ -291,5 +291,13 @@ export const membershipUserDALFactory = (db: TDbClient) => { } }; + // const listAvailableUsers = async (scopeData: AccessScopeData) => { + // try { + // const query = await db.replicaNode()(TableName.Membership).where(`${TableName.Membership}.scopeOrgId`); + // } catch (error) { + // throw new DatabaseError({ error, name: "ListAvailableUsers" }); + // } + // }; + return { ...orm, findUsers, getUserById }; }; diff --git a/backend/src/services/org/org-dal.ts b/backend/src/services/org/org-dal.ts index d7efdc463..fd1a361f0 100644 --- a/backend/src/services/org/org-dal.ts +++ b/backend/src/services/org/org-dal.ts @@ -65,7 +65,7 @@ export const orgDALFactory = (db: TDbClient) => { const buildBaseQuery = (orgIdSubquery: Knex.QueryBuilder) => { return db .replicaNode()(TableName.Organization) - .whereNull(`${TableName.Organization}.parentOrgId`) + .whereNull(`${TableName.Organization}.rootOrgId`) .whereIn(`${TableName.Organization}.id`, orgIdSubquery) .leftJoin(TableName.Project, `${TableName.Organization}.id`, `${TableName.Project}.orgId`) .leftJoin(TableName.Membership, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`) @@ -168,7 +168,7 @@ export const orgDALFactory = (db: TDbClient) => { // TODO(sub-org:group): check this when implement group support const query = db .replicaNode()(TableName.Organization) - .where(`${TableName.Organization}.parentOrgId`, dto.orgId) + .where(`${TableName.Organization}.rootOrgId`, dto.orgId) .select(selectAllTableCols(TableName.Organization)); if (dto.isAccessible) { @@ -196,7 +196,7 @@ export const orgDALFactory = (db: TDbClient) => { const org = (await db .replicaNode()(TableName.Organization) .where({ [`${TableName.Organization}.id` as "id"]: orgId }) - .whereNull(`${TableName.Organization}.parentOrgId`) + .whereNull(`${TableName.Organization}.rootOrgId`) .leftJoin(TableName.SamlConfig, (qb) => { qb.on(`${TableName.SamlConfig}.orgId`, "=", `${TableName.Organization}.id`).andOn( `${TableName.SamlConfig}.isActive`, @@ -233,7 +233,7 @@ export const orgDALFactory = (db: TDbClient) => { try { const org = (await db .replicaNode()(TableName.Organization) - .whereNull(`${TableName.Organization}.parentOrgId`) + .whereNull(`${TableName.Organization}.rootOrgId`) .where({ [`${TableName.Organization}.slug` as "slug"]: orgSlug }) .leftJoin(TableName.SamlConfig, (qb) => { qb.on(`${TableName.SamlConfig}.orgId`, "=", `${TableName.Organization}.id`).andOn( @@ -279,7 +279,7 @@ export const orgDALFactory = (db: TDbClient) => { .whereNotNull(`${TableName.Membership}.actorUserId`) .join(TableName.MembershipRole, `${TableName.Membership}.id`, `${TableName.MembershipRole}.membershipId`) .join(TableName.Organization, `${TableName.Membership}.scopeOrgId`, `${TableName.Organization}.id`) - .whereNull(`${TableName.Organization}.parentOrgId`) + .whereNull(`${TableName.Organization}.rootOrgId`) .leftJoin(TableName.SamlConfig, (qb) => { qb.on(`${TableName.SamlConfig}.orgId`, "=", `${TableName.Organization}.id`).andOn( `${TableName.SamlConfig}.isActive`, @@ -651,7 +651,7 @@ export const orgDALFactory = (db: TDbClient) => { }) .join(TableName.Users, `${TableName.Users}.id`, `${TableName.Membership}.actorUserId`) .join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.Membership}.scopeOrgId`) - .whereNull(`${TableName.Organization}.parentOrgId`) + .whereNull(`${TableName.Organization}.rootOrgId`) .leftJoin(TableName.UserAliases, function joinUserAlias() { this.on(`${TableName.UserAliases}.userId`, "=", `${TableName.Membership}.actorUserId`) .andOn(`${TableName.UserAliases}.orgId`, "=", `${TableName.Membership}.scopeOrgId`) @@ -690,7 +690,7 @@ export const orgDALFactory = (db: TDbClient) => { .replicaNode()(TableName.Membership) .where({ actorIdentityId: identityId }) .where(`${TableName.Membership}.scope`, AccessScope.Organization) - .whereNull(`${TableName.Organization}.parentOrgId`) + .whereNull(`${TableName.Organization}.rootOrgId`) .whereNotNull(`${TableName.Membership}.actorIdentityId`) .join(TableName.MembershipRole, `${TableName.Membership}.id`, `${TableName.MembershipRole}.membershipId`) .join(TableName.Organization, `${TableName.Membership}.scopeOrgId`, `${TableName.Organization}.id`) diff --git a/backend/src/services/org/org-service.ts b/backend/src/services/org/org-service.ts index 011af3520..76c1fb801 100644 --- a/backend/src/services/org/org-service.ts +++ b/backend/src/services/org/org-service.ts @@ -157,7 +157,7 @@ export const orgServiceFactory = ({ userId: string, orgId: string, actorAuthMethod: ActorAuthMethod, - parentOrgId: string, + rootOrgId: string, actorOrgId: string ) => { await permissionService.getOrgPermission({ @@ -165,17 +165,17 @@ export const orgServiceFactory = ({ actorId: userId, orgId, actorAuthMethod, - actorOrgId: parentOrgId, + actorOrgId: rootOrgId, scope: OrganizationActionScope.Any }); const appCfg = getConfig(); const org = await orgDAL.findOrgById(orgId); if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` }); - const hasSubOrg = actorOrgId !== parentOrgId; + const hasSubOrg = actorOrgId !== rootOrgId; let subOrg; if (hasSubOrg) { - subOrg = await orgDAL.findOne({ parentOrgId, id: actorOrgId }); + subOrg = await orgDAL.findOne({ rootOrgId, id: actorOrgId }); } if (!org.userTokenExpiration) { diff --git a/backend/src/services/project/project-service.ts b/backend/src/services/project/project-service.ts index e17787351..628e8891c 100644 --- a/backend/src/services/project/project-service.ts +++ b/backend/src/services/project/project-service.ts @@ -298,7 +298,6 @@ export const projectServiceFactory = ({ projectTemplate = await projectTemplateService.findProjectTemplateByName(template, { id: actorId, orgId: organization.id, - parentOrgId: organization.id, type: actor, authMethod: actorAuthMethod }); diff --git a/backend/src/services/service-token/service-token-service.ts b/backend/src/services/service-token/service-token-service.ts index 2aa495673..8b50e9970 100644 --- a/backend/src/services/service-token/service-token-service.ts +++ b/backend/src/services/service-token/service-token-service.ts @@ -25,10 +25,12 @@ import { TGetServiceTokenInfoDTO, TProjectServiceTokensDTO } from "./service-token-types"; +import { TOrgDALFactory } from "../org/org-dal"; type TServiceTokenServiceFactoryDep = { serviceTokenDAL: TServiceTokenDALFactory; userDAL: TUserDALFactory; + orgDAL: Pick; permissionService: Pick; projectEnvDAL: Pick; projectDAL: Pick; @@ -45,7 +47,8 @@ export const serviceTokenServiceFactory = ({ projectEnvDAL, projectDAL, accessTokenQueue, - smtpService + smtpService, + orgDAL }: TServiceTokenServiceFactoryDep) => { const createServiceToken = async ({ iv, @@ -184,7 +187,15 @@ export const serviceTokenServiceFactory = ({ if (!isMatch) throw new UnauthorizedError({ message: "Invalid service token" }); await accessTokenQueue.updateServiceTokenStatus(serviceToken.id); - return { ...serviceToken, lastUsed: new Date(), orgId: project.orgId }; + const serviceTokenOrgDetails = await orgDAL.findById(project.orgId); + + return { + ...serviceToken, + lastUsed: new Date(), + orgId: project.orgId, + parentOrgId: serviceTokenOrgDetails.parentOrgId || serviceTokenOrgDetails.id, + rootOrgId: serviceTokenOrgDetails.rootOrgId || serviceTokenOrgDetails.id + }; }; const notifyExpiringTokens = async () => {