diff --git a/api/src/controllers/listing.controller.ts b/api/src/controllers/listing.controller.ts index b0b524293c..b1c8edfd7f 100644 --- a/api/src/controllers/listing.controller.ts +++ b/api/src/controllers/listing.controller.ts @@ -64,7 +64,7 @@ import { ApiKeyGuard } from '../guards/api-key.guard'; PaginationAllowsAllQueryParams, IdDTO, ) -@UseGuards(ApiKeyGuard, OptionalAuthGuard) +@UseGuards(OptionalAuthGuard) @PermissionTypeDecorator('listing') @ActivityLogMetadata([{ targetPropertyName: 'status', propertyPath: 'status' }]) @UseInterceptors(ActivityLogInterceptor) @@ -92,7 +92,7 @@ export class ListingController { operationId: 'listAsCsv', }) @Header('Content-Type', 'application/zip') - @UseGuards(OptionalAuthGuard, PermissionGuard) + @UseGuards(ApiKeyGuard, OptionalAuthGuard, PermissionGuard) @UseInterceptors(ExportLogInterceptor) async listAsCsv( @Request() req: ExpressRequest, @@ -145,6 +145,7 @@ export class ListingController { @UseInterceptors(ClassSerializerInterceptor) @UsePipes(new ListingCreateUpdateValidationPipe(defaultValidationPipeOptions)) @ApiOkResponse({ type: Listing }) + @UseGuards(ApiKeyGuard) async create( @Request() req: ExpressRequest, @Body() listingDto: ListingCreate, @@ -158,6 +159,7 @@ export class ListingController { @Delete() @ApiOperation({ summary: 'Delete listing by id', operationId: 'delete' }) @UsePipes(new ValidationPipe(defaultValidationPipeOptions)) + @UseGuards(ApiKeyGuard) async delete( @Body() dto: IdDTO, @Request() req: ExpressRequest, @@ -173,7 +175,7 @@ export class ListingController { @ApiOkResponse({ type: SuccessDTO }) @PermissionAction(permissionActions.submit) @UseInterceptors(ActivityLogInterceptor) - @UseGuards(OptionalAuthGuard, AdminOrJurisdictionalAdminGuard) + @UseGuards(ApiKeyGuard, OptionalAuthGuard, AdminOrJurisdictionalAdminGuard) async process(): Promise { return await this.listingService.process(); } @@ -181,6 +183,7 @@ export class ListingController { @Put(':id') @ApiOperation({ summary: 'Update listing by id', operationId: 'update' }) @UsePipes(new ListingCreateUpdateValidationPipe(defaultValidationPipeOptions)) + @UseGuards(ApiKeyGuard) async update( @Request() req: ExpressRequest, @Param('id') listingId: string, diff --git a/api/test/integration/api-key-guard.e2e-spec.ts b/api/test/integration/api-key-guard.e2e-spec.ts index 70beb93df9..bce4aad946 100644 --- a/api/test/integration/api-key-guard.e2e-spec.ts +++ b/api/test/integration/api-key-guard.e2e-spec.ts @@ -1,12 +1,16 @@ import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication } from '@nestjs/common'; import request from 'supertest'; +import cookieParser from 'cookie-parser'; import { AppModule } from '../../src/modules/app.module'; import { PrismaService } from '../../src/services/prisma.service'; +import { Login } from '../../src/dtos/auth/login.dto'; +import { userFactory } from '../../prisma/seed-helpers/user-factory'; describe('API Key Guard Tests', () => { let app: INestApplication; let prisma: PrismaService; + let cookies = ''; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ @@ -15,7 +19,26 @@ describe('API Key Guard Tests', () => { app = moduleFixture.createNestApplication(); prisma = moduleFixture.get(PrismaService); + app.use(cookieParser()); await app.init(); + + const storedUser = await prisma.userAccounts.create({ + data: await userFactory({ + roles: { isAdmin: true }, + mfaEnabled: false, + confirmedAt: new Date(), + }), + }); + const resLogIn = await request(app.getHttpServer()) + .post('/auth/login') + .set({ passkey: process.env.API_PASS_KEY || '' }) + .send({ + email: storedUser.email, + password: 'abcdef', + } as Login) + .expect(201); + + cookies = resLogIn.headers['set-cookie']; }); afterAll(async () => { @@ -25,21 +48,26 @@ describe('API Key Guard Tests', () => { it('should succeed when correct header is present', async () => { await request(app.getHttpServer()) - .get('/jurisdictions') + .get('/reservedCommunityTypes') + .set('Cookie', cookies) .set({ passkey: process.env.API_PASS_KEY || '' }) .expect(200); }); it('should error when incorrect header is present', async () => { const res = await request(app.getHttpServer()) - .get('/listings') + .get('/reservedCommunityTypes') .set({ passkey: 'the wrong key' }) + .set('Cookie', cookies) .expect(401); expect(res.body.message).toBe('Traffic not from a known source'); }); it('should error when no header is present', async () => { - const res = await request(app.getHttpServer()).get('/listings').expect(401); + const res = await request(app.getHttpServer()) + .get('/reservedCommunityTypes') + .set('Cookie', cookies) + .expect(401); expect(res.body.message).toBe('Traffic not from a known source'); }); });