Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfd390f9

Browse files
authored
fix: add authorization checks to booking reassignment endpoints (#25054)
1 parenteae779b commitfd390f9

File tree

5 files changed

+139
-40
lines changed

5 files changed

+139
-40
lines changed

‎apps/api/v2/src/ee/bookings/2024-08-13/controllers/bookings.controller.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,9 @@ export class BookingsController_2024_08_13 {
405405
})
406406
asyncreassignBooking(
407407
@Param("bookingUid")bookingUid:string,
408-
@GetUser()user:ApiAuthGuardUser
408+
@GetUser()reassignedByUser:ApiAuthGuardUser
409409
):Promise<ReassignBookingOutput_2024_08_13>{
410-
constbooking=awaitthis.bookingsService.reassignBooking(bookingUid,user);
410+
constbooking=awaitthis.bookingsService.reassignBooking(bookingUid,reassignedByUser);
411411

412412
return{
413413
status:SUCCESS_STATUS,
@@ -430,13 +430,13 @@ export class BookingsController_2024_08_13 {
430430
asyncreassignBookingToUser(
431431
@Param("bookingUid")bookingUid:string,
432432
@Param("userId")userId:number,
433-
@GetUser("id")reassignedById:number,
433+
@GetUser()reassignedByUser:ApiAuthGuardUser,
434434
@Body()body:ReassignToUserBookingInput_2024_08_13
435435
):Promise<ReassignBookingOutput_2024_08_13>{
436436
constbooking=awaitthis.bookingsService.reassignBookingToUser(
437437
bookingUid,
438438
userId,
439-
reassignedById,
439+
reassignedByUser,
440440
body
441441
);
442442

‎apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/emails/team-emails.e2e-spec.ts‎

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { INestApplication } from "@nestjs/common";
1212
import{NestExpressApplication}from"@nestjs/platform-express";
1313
import{Test}from"@nestjs/testing";
1414
import*asrequestfrom"supertest";
15+
import{ApiKeysRepositoryFixture}from"test/fixtures/repository/api-keys.repository.fixture";
1516
import{EventTypesRepositoryFixture}from"test/fixtures/repository/event-types.repository.fixture";
1617
import{HostsRepositoryFixture}from"test/fixtures/repository/hosts.repository.fixture";
1718
import{MembershipRepositoryFixture}from"test/fixtures/repository/membership.repository.fixture";
@@ -20,7 +21,6 @@ import { ProfileRepositoryFixture } from "test/fixtures/repository/profiles.repo
2021
import{TeamRepositoryFixture}from"test/fixtures/repository/team.repository.fixture";
2122
import{UserRepositoryFixture}from"test/fixtures/repository/users.repository.fixture";
2223
import{randomString}from"test/utils/randomString";
23-
import{withApiAuth}from"test/utils/withApiAuth";
2424

2525
import{CAL_API_VERSION_HEADER,SUCCESS_STATUS,VERSION_2024_08_13}from"@calcom/platform-constants";
2626
import{
@@ -72,6 +72,7 @@ type EmailSetup = {
7272
team:Team;
7373
member1:User;
7474
member2:User;
75+
member1ApiKey:string;
7576
collectiveEventType:{id:number};
7677
roundRobinEventType:{id:number};
7778
};
@@ -89,25 +90,21 @@ describe("Bookings Endpoints 2024-08-13 team emails", () => {
8990
letprofileRepositoryFixture:ProfileRepositoryFixture;
9091
letmembershipsRepositoryFixture:MembershipRepositoryFixture;
9192
lethostsRepositoryFixture:HostsRepositoryFixture;
93+
letapiKeysRepositoryFixture:ApiKeysRepositoryFixture;
9294

9395
// Setup data for tests
9496
letemailsEnabledSetup:EmailSetup;
9597
letemailsDisabledSetup:EmailSetup;
9698

97-
constauthEmail="team-emails-2024-08-13-user-admin@example.com";
98-
9999
// Utility function to check response data type
100-
constresponseDataIsBooking=(data:any):data isBookingOutput_2024_08_13=>{
101-
return!Array.isArray(data)&&typeofdata==="object"&&data&&"id"indata;
100+
constresponseDataIsBooking=(data:unknown):data isBookingOutput_2024_08_13=>{
101+
return!Array.isArray(data)&&data!==null&&typeofdata==="object"&&data&&"id"indata;
102102
};
103103

104104
beforeAll(async()=>{
105-
constmoduleRef=awaitwithApiAuth(
106-
authEmail,
107-
Test.createTestingModule({
108-
imports:[AppModule,PrismaModule,UsersModule,SchedulesModule_2024_04_15],
109-
})
110-
)
105+
constmoduleRef=awaitTest.createTestingModule({
106+
imports:[AppModule,PrismaModule,UsersModule,SchedulesModule_2024_04_15],
107+
})
111108
.overrideGuard(PermissionsGuard)
112109
.useValue({
113110
canActivate:()=>true,
@@ -122,6 +119,7 @@ describe("Bookings Endpoints 2024-08-13 team emails", () => {
122119
profileRepositoryFixture=newProfileRepositoryFixture(moduleRef);
123120
membershipsRepositoryFixture=newMembershipRepositoryFixture(moduleRef);
124121
hostsRepositoryFixture=newHostsRepositoryFixture(moduleRef);
122+
apiKeysRepositoryFixture=newApiKeysRepositoryFixture(moduleRef);
125123
schedulesService=moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15);
126124

127125
// Create a base organization for all tests
@@ -131,11 +129,6 @@ describe("Bookings Endpoints 2024-08-13 team emails", () => {
131129
emailsEnabledSetup=awaitsetupTestEnvironment(true);
132130
emailsDisabledSetup=awaitsetupTestEnvironment(false);
133131

134-
awaituserRepositoryFixture.create({
135-
email:authEmail,
136-
organization:{connect:{id:organization.id}},
137-
});
138-
139132
app=moduleRef.createNestApplication();
140133
bootstrap(appasNestExpressApplication);
141134
awaitapp.init();
@@ -173,10 +166,15 @@ describe("Bookings Endpoints 2024-08-13 team emails", () => {
173166
constcollectiveEvent=awaitcreateEventType("COLLECTIVE",team.id,[member1.id,member2.id]);
174167
constroundRobinEvent=awaitcreateEventType("ROUND_ROBIN",team.id,[member1.id,member2.id]);
175168

169+
// Create API key for member1 to use in authorized tests
170+
const{ keyString}=awaitapiKeysRepositoryFixture.createApiKey(member1.id,null);
171+
constmember1ApiKey=`cal_test_${keyString}`;
172+
176173
return{
177174
team,
178175
member1,
179176
member2,
177+
member1ApiKey,
180178
collectiveEventType:{id:collectiveEvent.id},
181179
roundRobinEventType:{id:roundRobinEvent.id},
182180
};
@@ -351,6 +349,7 @@ describe("Bookings Endpoints 2024-08-13 team emails", () => {
351349
:emailsDisabledSetup.member1.id;
352350
constmanualReassignResponse=awaitrequest(app.getHttpServer())
353351
.post(`/v2/bookings/${rescheduledBookingUid}/reassign/${reassignToId}`)
352+
.set("Authorization",`Bearer${emailsDisabledSetup.member1ApiKey}`)
354353
.set(CAL_API_VERSION_HEADER,VERSION_2024_08_13);
355354

356355
expect(manualReassignResponse.status).toBe(200);
@@ -359,6 +358,7 @@ describe("Bookings Endpoints 2024-08-13 team emails", () => {
359358
// --- 4. Automatic Reassign ---
360359
constautoReassignResponse=awaitrequest(app.getHttpServer())
361360
.post(`/v2/bookings/${rescheduledBookingUid}/reassign`)
361+
.set("Authorization",`Bearer${emailsDisabledSetup.member1ApiKey}`)
362362
.set(CAL_API_VERSION_HEADER,VERSION_2024_08_13);
363363

364364
expect(autoReassignResponse.status).toBe(200);
@@ -484,6 +484,7 @@ describe("Bookings Endpoints 2024-08-13 team emails", () => {
484484

485485
constmanualReassignResponse=awaitrequest(app.getHttpServer())
486486
.post(`/v2/bookings/${rescheduledBookingUid}/reassign/${reassignToId}`)
487+
.set("Authorization",`Bearer${emailsEnabledSetup.member1ApiKey}`)
487488
.set(CAL_API_VERSION_HEADER,VERSION_2024_08_13);
488489

489490
expect(manualReassignResponse.status).toBe(200);
@@ -499,6 +500,7 @@ describe("Bookings Endpoints 2024-08-13 team emails", () => {
499500
jest.clearAllMocks();
500501
constautoReassignResponse=awaitrequest(app.getHttpServer())
501502
.post(`/v2/bookings/${rescheduledBookingUid}/reassign`)
503+
.set("Authorization",`Bearer${emailsEnabledSetup.member1ApiKey}`)
502504
.set(CAL_API_VERSION_HEADER,VERSION_2024_08_13);
503505

504506
expect(autoReassignResponse.status).toBe(200);
@@ -523,7 +525,6 @@ describe("Bookings Endpoints 2024-08-13 team emails", () => {
523525
afterAll(async()=>{
524526
// Clean up database records
525527
awaitteamRepositoryFixture.delete(organization.id);
526-
awaituserRepositoryFixture.deleteByEmail(authEmail);
527528
awaituserRepositoryFixture.deleteByEmail(emailsEnabledSetup.member1.email);
528529
awaituserRepositoryFixture.deleteByEmail(emailsEnabledSetup.member2.email);
529530
awaituserRepositoryFixture.deleteByEmail(emailsDisabledSetup.member1.email);

‎apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/reassign-bookings.e2e-spec.ts‎

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import{bootstrap}from"@/app";
22
import{AppModule}from"@/app.module";
33
import{ReassignBookingOutput_2024_08_13}from"@/ee/bookings/2024-08-13/outputs/reassign-booking.output";
4+
import{BOOKING_REASSIGN_PERMISSION_ERROR}from"@/ee/bookings/2024-08-13/services/bookings.service";
45
import{CreateScheduleInput_2024_04_15}from"@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input";
56
import{SchedulesModule_2024_04_15}from"@/ee/schedules/schedules_2024_04_15/schedules.module";
67
import{SchedulesService_2024_04_15}from"@/ee/schedules/schedules_2024_04_15/services/schedules.service";
@@ -11,6 +12,7 @@ import { INestApplication } from "@nestjs/common";
1112
import{NestExpressApplication}from"@nestjs/platform-express";
1213
import{Test}from"@nestjs/testing";
1314
import*asrequestfrom"supertest";
15+
import{ApiKeysRepositoryFixture}from"test/fixtures/repository/api-keys.repository.fixture";
1416
import{BookingsRepositoryFixture}from"test/fixtures/repository/bookings.repository.fixture";
1517
import{EventTypesRepositoryFixture}from"test/fixtures/repository/event-types.repository.fixture";
1618
import{HostsRepositoryFixture}from"test/fixtures/repository/hosts.repository.fixture";
@@ -21,15 +23,11 @@ import { ProfileRepositoryFixture } from "test/fixtures/repository/profiles.repo
2123
import{TeamRepositoryFixture}from"test/fixtures/repository/team.repository.fixture";
2224
import{UserRepositoryFixture}from"test/fixtures/repository/users.repository.fixture";
2325
import{randomString}from"test/utils/randomString";
24-
import{withApiAuth}from"test/utils/withApiAuth";
25-
26-
2726

2827
import{CAL_API_VERSION_HEADER,SUCCESS_STATUS,VERSION_2024_08_13}from"@calcom/platform-constants";
2928
importtype{CreateBookingInput_2024_08_13}from"@calcom/platform-types";
3029
importtype{Booking,User,PlatformOAuthClient,Team}from"@calcom/prisma/client";
3130

32-
3331
describe("Bookings Endpoints 2024-08-13",()=>{
3432
describe("Reassign bookings",()=>{
3533
letapp:INestApplication;
@@ -47,13 +45,16 @@ describe("Bookings Endpoints 2024-08-13", () => {
4745
lethostsRepositoryFixture:HostsRepositoryFixture;
4846
letorganizationsRepositoryFixture:OrganizationRepositoryFixture;
4947
letprofileRepositoryFixture:ProfileRepositoryFixture;
48+
letapiKeysRepositoryFixture:ApiKeysRepositoryFixture;
5049

5150
constteamUserEmail=`reassign-bookings-2024-08-13-user1-${randomString()}@api.com`;
5251
constteamUserEmail2=`reassign-bookings-2024-08-13-user2-${randomString()}@api.com`;
5352
constteamUserEmail3=`reassign-bookings-2024-08-13-user3-${randomString()}@api.com`;
5453
letteamUser1:User;
5554
letteamUser2:User;
5655
letteamUser3:User;
56+
letteamUser1ApiKey:string;
57+
letteamUser2ApiKey:string;
5758

5859
letteamRoundRobinEventTypeId:number;
5960
letteamRoundRobinFixedHostEventTypeId:number;
@@ -69,12 +70,9 @@ describe("Bookings Endpoints 2024-08-13", () => {
6970
letrescheduleReasonBookingInitialHostId:number;
7071

7172
beforeAll(async()=>{
72-
constmoduleRef=awaitwithApiAuth(
73-
teamUserEmail,
74-
Test.createTestingModule({
75-
imports:[AppModule,PrismaModule,UsersModule,SchedulesModule_2024_04_15],
76-
})
77-
)
73+
constmoduleRef=awaitTest.createTestingModule({
74+
imports:[AppModule,PrismaModule,UsersModule,SchedulesModule_2024_04_15],
75+
})
7876
.overrideGuard(PermissionsGuard)
7977
.useValue({
8078
canActivate:()=>true,
@@ -90,6 +88,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
9088
profileRepositoryFixture=newProfileRepositoryFixture(moduleRef);
9189
membershipsRepositoryFixture=newMembershipRepositoryFixture(moduleRef);
9290
hostsRepositoryFixture=newHostsRepositoryFixture(moduleRef);
91+
apiKeysRepositoryFixture=newApiKeysRepositoryFixture(moduleRef);
9392
schedulesService=moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15);
9493

9594
organization=awaitorganizationsRepositoryFixture.create({
@@ -126,6 +125,12 @@ describe("Bookings Endpoints 2024-08-13", () => {
126125
name:`reassign-bookings-2024-08-13-user3-${randomString()}`,
127126
});
128127

128+
const{ keyString}=awaitapiKeysRepositoryFixture.createApiKey(teamUser1.id,null);
129+
teamUser1ApiKey=`cal_test_${keyString}`;
130+
131+
const{keyString:keyString2}=awaitapiKeysRepositoryFixture.createApiKey(teamUser2.id,null);
132+
teamUser2ApiKey=`cal_test_${keyString2}`;
133+
129134
constuserSchedule:CreateScheduleInput_2024_04_15={
130135
name:`reassign-bookings-2024-08-13-schedule-${randomString()}`,
131136
timeZone:"Europe/Rome",
@@ -473,6 +478,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
473478

474479
returnrequest(app.getHttpServer())
475480
.post(`/v2/bookings/${roundRobinBooking.uid}/reassign`)
481+
.set("Authorization",`Bearer${teamUser1ApiKey}`)
476482
.set(CAL_API_VERSION_HEADER,VERSION_2024_08_13)
477483
.expect(200)
478484
.then(async(response)=>{
@@ -512,6 +518,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
512518
returnrequest(app.getHttpServer())
513519
.post(`/v2/bookings/${roundRobinBooking.uid}/reassign/${teamUser1.id}`)
514520
.send(body)
521+
.set("Authorization",`Bearer${teamUser1ApiKey}`)
515522
.set(CAL_API_VERSION_HEADER,VERSION_2024_08_13)
516523
.expect(200)
517524
.then(async(response)=>{
@@ -562,6 +569,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
562569

563570
returnrequest(app.getHttpServer())
564571
.post(`/v2/bookings/${bookingUid}/reassign`)
572+
.set("Authorization",`Bearer${teamUser2ApiKey}`)
565573
.set(CAL_API_VERSION_HEADER,VERSION_2024_08_13)
566574
.expect(200)
567575
.then(async(response)=>{
@@ -611,6 +619,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
611619

612620
returnrequest(app.getHttpServer())
613621
.post(`/v2/bookings/${bookingUid}/reassign/${teamUser3.id}`)
622+
.set("Authorization",`Bearer${teamUser1ApiKey}`)
614623
.set(CAL_API_VERSION_HEADER,VERSION_2024_08_13)
615624
.expect(200)
616625
.then(async(response)=>{
@@ -669,6 +678,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
669678

670679
returnrequest(app.getHttpServer())
671680
.post(`/v2/bookings/${bookingUid}/reassign/${reassignToHostId}`)
681+
.set("Authorization",`Bearer${teamUser1ApiKey}`)
672682
.set(CAL_API_VERSION_HEADER,VERSION_2024_08_13)
673683
.expect(200)
674684
.then(async(response)=>{
@@ -704,6 +714,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
704714

705715
returnrequest(app.getHttpServer())
706716
.post(`/v2/bookings/${bookingUid}/reassign`)
717+
.set("Authorization",`Bearer${teamUser1ApiKey}`)
707718
.set(CAL_API_VERSION_HEADER,VERSION_2024_08_13)
708719
.expect(200)
709720
.then(async(response)=>{
@@ -724,6 +735,51 @@ describe("Bookings Endpoints 2024-08-13", () => {
724735
});
725736
});
726737

738+
it("should return 403 when unauthorized user tries to reassign booking",async()=>{
739+
constunauthorizedUserEmail=`fake-user-${randomString()}@api.com`;
740+
constunauthorizedUser=awaituserRepositoryFixture.create({
741+
email:unauthorizedUserEmail,
742+
locale:"en",
743+
name:`fake-user-${randomString()}`,
744+
});
745+
746+
const{ keyString}=awaitapiKeysRepositoryFixture.createApiKey(unauthorizedUser.id,null);
747+
constunauthorizedApiKeyString=`cal_test_${keyString}`;
748+
749+
constresponse=awaitrequest(app.getHttpServer())
750+
.post(`/v2/bookings/${roundRobinBooking.uid}/reassign`)
751+
.set("Authorization",`Bearer${unauthorizedApiKeyString}`)
752+
.set(CAL_API_VERSION_HEADER,VERSION_2024_08_13)
753+
.expect(403);
754+
755+
expect(response.body.error.message).toBe(BOOKING_REASSIGN_PERMISSION_ERROR);
756+
757+
awaituserRepositoryFixture.deleteByEmail(unauthorizedUserEmail);
758+
});
759+
760+
it("should return 403 when unauthorized user tries to reassign booking to specific user",async()=>{
761+
constunauthorizedUserEmail=`fake-user-${randomString()}@api.com`;
762+
constunauthorizedUser=awaituserRepositoryFixture.create({
763+
email:unauthorizedUserEmail,
764+
locale:"en",
765+
name:`fake-user-${randomString()}`,
766+
});
767+
768+
const{ keyString}=awaitapiKeysRepositoryFixture.createApiKey(unauthorizedUser.id,null);
769+
constunauthorizedApiKeyString=`cal_test_${keyString}`;
770+
771+
constresponse=awaitrequest(app.getHttpServer())
772+
.post(`/v2/bookings/${roundRobinBooking.uid}/reassign/${teamUser2.id}`)
773+
.send({reason:"Testing unauthorized access"})
774+
.set("Authorization",`Bearer${unauthorizedApiKeyString}`)
775+
.set(CAL_API_VERSION_HEADER,VERSION_2024_08_13)
776+
.expect(403);
777+
778+
expect(response.body.error.message).toBe(BOOKING_REASSIGN_PERMISSION_ERROR);
779+
780+
awaituserRepositoryFixture.deleteByEmail(unauthorizedUserEmail);
781+
});
782+
727783
asyncfunctioncreateOAuthClient(organizationId:number){
728784
constdata={
729785
logo:"logo-url",
@@ -749,4 +805,4 @@ describe("Bookings Endpoints 2024-08-13", () => {
749805
awaitapp.close();
750806
});
751807
});
752-
});
808+
});

‎apps/api/v2/src/ee/bookings/2024-08-13/repositories/bookings.repository.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,17 @@ export class BookingsRepository_2024_08_13 {
7272
});
7373
}
7474

75+
asyncgetByUidWithEventType(bookingUid:string){
76+
returnthis.dbRead.prisma.booking.findUnique({
77+
where:{
78+
uid:bookingUid,
79+
},
80+
include:{
81+
eventType:true,
82+
},
83+
});
84+
}
85+
7586
asyncgetByUidWithUser(bookingUid:string){
7687
returnthis.dbRead.prisma.booking.findUnique({
7788
where:{

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp