Skip to content

Commit 00dfdc0

Browse files
Fix a bug with the access to the expenses list of a campaign. (#545)
We used to honour only the coordinator, but the organizer is as important.
1 parent 91b86ba commit 00dfdc0

File tree

3 files changed

+25
-23
lines changed

3 files changed

+25
-23
lines changed

apps/api/src/campaign-file/campaign-file.controller.spec.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ describe('CampaignFileController', () => {
4848
ConfigService,
4949
{
5050
provide: CampaignService,
51-
useValue: { getCampaignByIdAndCoordinatorId: jest.fn(() => null) },
51+
useValue: { verifyCampaignOwner: jest.fn(() => null) },
5252
},
5353
VaultService,
5454
CampaignNewsService,
@@ -92,7 +92,7 @@ describe('CampaignFileController', () => {
9292
).toEqual([fileId, fileId])
9393

9494
expect(personService.findOneByKeycloakId).toHaveBeenCalledWith(userMock.sub)
95-
expect(campaignService.getCampaignByIdAndCoordinatorId).not.toHaveBeenCalled()
95+
expect(campaignService.verifyCampaignOwner).not.toHaveBeenCalled()
9696
expect(campaignFileService.create).toHaveBeenCalledTimes(2)
9797
})
9898

@@ -102,16 +102,13 @@ describe('CampaignFileController', () => {
102102
await expect(controller.create(campaignId, { roles: [] }, [], userMock)).rejects.toThrowError()
103103

104104
expect(personService.findOneByKeycloakId).toHaveBeenCalledWith(userMock.sub)
105-
expect(campaignService.getCampaignByIdAndCoordinatorId).not.toHaveBeenCalled()
105+
expect(campaignService.verifyCampaignOwner).not.toHaveBeenCalled()
106106
})
107107

108108
it('should throw an error for user not owning updated campaign', async () => {
109109
await expect(controller.create(campaignId, { roles: [] }, [], userMock)).rejects.toThrowError()
110110

111111
expect(personService.findOneByKeycloakId).toHaveBeenCalledWith(userMock.sub)
112-
expect(campaignService.getCampaignByIdAndCoordinatorId).toHaveBeenCalledWith(
113-
campaignId,
114-
personIdMock,
115-
)
112+
expect(campaignService.verifyCampaignOwner).toHaveBeenCalledWith(campaignId, personIdMock)
116113
})
117114
})

apps/api/src/campaign-file/campaign-file.controller.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { FilesRoleDto } from './dto/files-role.dto'
2323
import { CampaignFileService } from './campaign-file.service'
2424
import { CampaignService } from '../campaign/campaign.service'
2525
import { KeycloakTokenParsed, isAdmin } from '../auth/keycloak'
26-
import { ApiTags } from '@nestjs/swagger';
26+
import { ApiTags } from '@nestjs/swagger'
2727
import { CampaignFileRole } from '@prisma/client'
2828

2929
@ApiTags('campaign-file')
@@ -51,10 +51,7 @@ export class CampaignFileController {
5151
}
5252

5353
if (!isAdmin(user)) {
54-
const campaign = await this.campaignService.getCampaignByIdAndCoordinatorId(
55-
campaignId,
56-
person.id,
57-
)
54+
const campaign = await this.campaignService.verifyCampaignOwner(campaignId, person.id)
5855
if (!campaign) {
5956
throw new NotFoundException(
6057
'User ' + user.name + 'is not admin or coordinator of campaign with id: ' + campaignId,
@@ -88,8 +85,8 @@ export class CampaignFileController {
8885
'Content-Type': file.mimetype,
8986
'Content-Disposition': 'attachment; filename="' + file.filename + '"',
9087
'Cache-Control': file.mimetype.startsWith('image/')
91-
? 'public, s-maxage=15552000, stale-while-revalidate=15552000, immutable'
92-
: 'no-store'
88+
? 'public, s-maxage=15552000, stale-while-revalidate=15552000, immutable'
89+
: 'no-store',
9390
})
9491

9592
return new StreamableFile(file.stream)

apps/api/src/campaign/campaign.service.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -235,15 +235,23 @@ export class CampaignService {
235235
return campaigns
236236
}
237237

238-
async getCampaignByIdAndCoordinatorId(
239-
campaignId: string,
240-
coordinatorId: string,
241-
): Promise<Campaign | null> {
242-
const campaign = await this.prisma.campaign.findFirst({
243-
where: { id: campaignId, coordinator: { personId: coordinatorId } },
244-
include: { coordinator: true },
238+
// Check if the campaign exists by coordinator or organizer
239+
async verifyCampaignOwner(campaignId: string, personId: string): Promise<Campaign | null> {
240+
const campaignByCoordinator = await this.prisma.campaign.findFirst({
241+
where: { id: campaignId, coordinator: { personId } },
242+
include: { coordinator: true, organizer: true },
245243
})
246-
return campaign
244+
245+
if (campaignByCoordinator !== null) {
246+
return campaignByCoordinator
247+
}
248+
249+
const campaignByOrganizer = await this.prisma.campaign.findFirst({
250+
where: { id: campaignId, organizer: { personId } },
251+
include: { coordinator: true, organizer: true },
252+
})
253+
254+
return campaignByOrganizer
247255
}
248256

249257
async getCampaignByIdWithPersonIds(id: string) {
@@ -1070,7 +1078,7 @@ export class CampaignService {
10701078
throw new UnauthorizedException()
10711079
}
10721080

1073-
const campaign = await this.getCampaignByIdAndCoordinatorId(campaignId, person.id)
1081+
const campaign = await this.verifyCampaignOwner(campaignId, person.id)
10741082
if (!campaign) {
10751083
throw new UnauthorizedException()
10761084
}

0 commit comments

Comments
 (0)