Skip to content

Commit a2d0cbf

Browse files
authored
chore: v2 dont allow mixing platform teams/users with non-platform (calcom#20296)
* chore: v2 dont allow mixing platform teams/users with non-platform * chore: finish main merge * test: e2e * refactor: simplify can add user to team logic * refactor: fetch only needed oauth data * refactor: cleanup
1 parent d65c075 commit a2d0cbf

8 files changed

Lines changed: 562 additions & 7 deletions

File tree

apps/api/v2/src/modules/oauth-clients/oauth-client.repository.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ export class OAuthClientRepository {
119119
});
120120
}
121121

122+
async getByOrgId(organizationId: number) {
123+
return this.dbRead.prisma.platformOAuthClient.findMany({
124+
where: {
125+
organizationId,
126+
},
127+
select: { id: true },
128+
});
129+
}
130+
122131
async getByEventTypeHosts(eventTypeId: number) {
123132
const hostWithUserPlatformClient = await this.dbRead.prisma.host.findFirst({
124133
select: {

apps/api/v2/src/modules/organizations/memberships/services/organizations-membership.service.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
1+
import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
12
import { CreateOrgMembershipDto } from "@/modules/organizations/memberships/inputs/create-organization-membership.input";
23
import { OrganizationsMembershipRepository } from "@/modules/organizations/memberships/organizations-membership.repository";
3-
import { Injectable, NotFoundException } from "@nestjs/common";
4+
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
45

56
import { TeamService } from "@calcom/platform-libraries";
67

78
import { UpdateOrgMembershipDto } from "../inputs/update-organization-membership.input";
89
import { OrganizationsMembershipOutputService } from "./organizations-membership-output.service";
910

11+
export const PLATFORM_USER_BEING_ADDED_TO_REGULAR_ORG_ERROR = `Can't add user to organization - the user is platform managed user but organization is not because organization probably was not created using OAuth credentials.`;
12+
export const REGULAR_USER_BEING_ADDED_TO_PLATFORM_ORG_ERROR = `Can't add user to organization - the user is not platform managed user but organization is platform managed. Both have to be created using OAuth credentials.`;
13+
export const MANAGED_USER_AND_MANAGED_ORG_CREATED_WITH_DIFFERENT_OAUTH_CLIENTS_ERROR = `Can't add user to organization - managed user and organization were created using different OAuth clients.`;
14+
1015
@Injectable()
1116
export class OrganizationsMembershipService {
1217
constructor(
1318
private readonly organizationsMembershipRepository: OrganizationsMembershipRepository,
14-
private readonly organizationsMembershipOutputService: OrganizationsMembershipOutputService
19+
private readonly organizationsMembershipOutputService: OrganizationsMembershipOutputService,
20+
private readonly oAuthClientsRepository: OAuthClientRepository
1521
) {}
1622

1723
async getOrgMembership(organizationId: number, membershipId: number) {
@@ -95,7 +101,33 @@ export class OrganizationsMembershipService {
95101
}
96102

97103
async createOrgMembership(organizationId: number, data: CreateOrgMembershipDto) {
104+
await this.canUserBeAddedToOrg(data.userId, organizationId);
98105
const membership = await this.organizationsMembershipRepository.createOrgMembership(organizationId, data);
99106
return this.organizationsMembershipOutputService.getOrgMembershipOutput(membership);
100107
}
108+
109+
async canUserBeAddedToOrg(userId: number, orgId: number) {
110+
const [userOAuthClient, orgOAuthClients] = await Promise.all([
111+
this.oAuthClientsRepository.getByUserId(userId),
112+
this.oAuthClientsRepository.getByOrgId(orgId),
113+
]);
114+
115+
if (!userOAuthClient && orgOAuthClients.length === 0) {
116+
return true;
117+
}
118+
119+
if (userOAuthClient && orgOAuthClients.some((orgClient) => orgClient.id === userOAuthClient.id)) {
120+
return true;
121+
}
122+
123+
if (!userOAuthClient) {
124+
throw new BadRequestException(REGULAR_USER_BEING_ADDED_TO_PLATFORM_ORG_ERROR);
125+
}
126+
127+
if (orgOAuthClients.length === 0) {
128+
throw new BadRequestException(PLATFORM_USER_BEING_ADDED_TO_REGULAR_ORG_ERROR);
129+
}
130+
131+
throw new BadRequestException(MANAGED_USER_AND_MANAGED_ORG_CREATED_WITH_DIFFERENT_OAUTH_CLIENTS_ERROR);
132+
}
101133
}

apps/api/v2/src/modules/organizations/organizations.module.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import { ZoomVideoService } from "@/modules/conferencing/services/zoom-video.ser
1313
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
1414
import { EmailModule } from "@/modules/email/email.module";
1515
import { EmailService } from "@/modules/email/email.service";
16-
import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
16+
import { MembershipsModule } from "@/modules/memberships/memberships.module";
17+
import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
1718
import { UserOOORepository } from "@/modules/ooo/repositories/ooo.repository";
1819
import { UserOOOService } from "@/modules/ooo/services/ooo.service";
1920
import { OrganizationsAttributesController } from "@/modules/organizations/attributes/index/controllers/organizations-attributes.controller";
@@ -65,6 +66,8 @@ import { RedisModule } from "@/modules/redis/redis.module";
6566
import { RedisService } from "@/modules/redis/redis.service";
6667
import { StripeModule } from "@/modules/stripe/stripe.module";
6768
import { TeamsEventTypesModule } from "@/modules/teams/event-types/teams-event-types.module";
69+
import { TeamsMembershipsService } from "@/modules/teams/memberships/services/teams-memberships.service";
70+
import { TeamsMembershipsRepository } from "@/modules/teams/memberships/teams-memberships.repository";
6871
import { TeamsSchedulesService } from "@/modules/teams/schedules/services/teams-schedules.service";
6972
import { TeamsModule } from "@/modules/teams/teams/teams.module";
7073
import { TokensRepository } from "@/modules/tokens/tokens.repository";
@@ -93,6 +96,7 @@ import { Module } from "@nestjs/common";
9396
OrganizationsOrganizationsModule,
9497
OrganizationsStripeModule,
9598
OrganizationsTeamsRoutingFormsModule,
99+
MembershipsModule,
96100
OrganizationsConferencingModule,
97101
EventTypesPrivateLinksModule,
98102
],
@@ -101,7 +105,6 @@ import { Module } from "@nestjs/common";
101105
OrganizationsTeamsRepository,
102106
OrganizationsService,
103107
OrganizationsTeamsService,
104-
MembershipsRepository,
105108
OrganizationsSchedulesService,
106109
OrganizationsUsersRepository,
107110
OrganizationsUsersService,
@@ -147,6 +150,9 @@ import { Module } from "@nestjs/common";
147150
TeamsSchedulesService,
148151
SchedulesService_2024_06_11,
149152
InputSchedulesService_2024_06_11,
153+
TeamsMembershipsService,
154+
TeamsMembershipsRepository,
155+
OAuthClientRepository,
150156
],
151157
exports: [
152158
OrganizationsService,

0 commit comments

Comments
 (0)