authentication authorization
This commit is contained in:
@@ -1,7 +1,16 @@
|
||||
import 'reflect-metadata';
|
||||
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import request from 'supertest';
|
||||
import { vi } from 'vitest';
|
||||
import { LeagueController } from './LeagueController';
|
||||
import { LeagueService } from './LeagueService';
|
||||
import { AuthenticationGuard } from '../auth/AuthenticationGuard';
|
||||
import { AuthorizationGuard } from '../auth/AuthorizationGuard';
|
||||
import type { AuthorizationService } from '../auth/AuthorizationService';
|
||||
import { FeatureAvailabilityGuard } from '../policy/FeatureAvailabilityGuard';
|
||||
import type { PolicyService, PolicySnapshot } from '../policy/PolicyService';
|
||||
|
||||
describe('LeagueController', () => {
|
||||
let controller: LeagueController;
|
||||
@@ -55,4 +64,75 @@ describe('LeagueController', () => {
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(leagueService.getLeagueStandings).toHaveBeenCalledWith('league-1');
|
||||
});
|
||||
|
||||
describe('auth guards (HTTP)', () => {
|
||||
let app: any;
|
||||
|
||||
const sessionPort: { getCurrentSession: () => Promise<null | { token: string; user: { id: string } }> } = {
|
||||
getCurrentSession: vi.fn(async () => null),
|
||||
};
|
||||
|
||||
const authorizationService: Pick<AuthorizationService, 'getRolesForUser'> = {
|
||||
getRolesForUser: vi.fn(() => []),
|
||||
};
|
||||
|
||||
const policyService: Pick<PolicyService, 'getSnapshot'> = {
|
||||
getSnapshot: vi.fn(async (): Promise<PolicySnapshot> => ({
|
||||
policyVersion: 1,
|
||||
operationalMode: 'normal',
|
||||
maintenanceAllowlist: { view: [], mutate: [] },
|
||||
capabilities: {},
|
||||
loadedFrom: 'defaults',
|
||||
loadedAtIso: new Date(0).toISOString(),
|
||||
})),
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
controllers: [LeagueController],
|
||||
providers: [
|
||||
{
|
||||
provide: LeagueService,
|
||||
useValue: {
|
||||
getTotalLeagues: vi.fn(async () => ({ totalLeagues: 0 })),
|
||||
getLeagueAdmin: vi.fn(async () => ({}) ),
|
||||
},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
|
||||
const reflector = new Reflector();
|
||||
app.useGlobalGuards(
|
||||
new AuthenticationGuard(sessionPort as any),
|
||||
new AuthorizationGuard(reflector, authorizationService as any),
|
||||
new FeatureAvailabilityGuard(reflector, policyService as any),
|
||||
);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app?.close();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('allows @Public() endpoint without a session', async () => {
|
||||
await request(app.getHttpServer()).get('/leagues/total-leagues').expect(200);
|
||||
});
|
||||
|
||||
it('denies non-public endpoint by default when not authenticated (401)', async () => {
|
||||
await request(app.getHttpServer()).get('/leagues/l1/admin').expect(401);
|
||||
});
|
||||
|
||||
it('allows non-public endpoint when authenticated via session port', async () => {
|
||||
vi.mocked(sessionPort.getCurrentSession).mockResolvedValueOnce({
|
||||
token: 't',
|
||||
user: { id: 'user-1' },
|
||||
});
|
||||
|
||||
await request(app.getHttpServer()).get('/leagues/l1/admin').expect(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Body, Controller, Get, Param, Patch, Post, Inject } from '@nestjs/common';
|
||||
import { ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { Public } from '../auth/Public';
|
||||
import { LeagueService } from './LeagueService';
|
||||
import { AllLeaguesWithCapacityDTO } from './dtos/AllLeaguesWithCapacityDTO';
|
||||
import { ApproveJoinRequestInputDTO } from './dtos/ApproveJoinRequestInputDTO';
|
||||
@@ -40,6 +41,7 @@ import { LeagueScoringPresetsDTO } from './dtos/LeagueScoringPresetsDTO';
|
||||
export class LeagueController {
|
||||
constructor(@Inject(LeagueService) private readonly leagueService: LeagueService) {}
|
||||
|
||||
@Public()
|
||||
@Get('all-with-capacity')
|
||||
@ApiOperation({ summary: 'Get all leagues with their capacity information' })
|
||||
@ApiResponse({ status: 200, description: 'List of leagues with capacity', type: AllLeaguesWithCapacityDTO })
|
||||
@@ -47,6 +49,7 @@ export class LeagueController {
|
||||
return this.leagueService.getAllLeaguesWithCapacity();
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get('total-leagues')
|
||||
@ApiOperation({ summary: 'Get the total number of leagues' })
|
||||
@ApiResponse({ status: 200, description: 'Total number of leagues', type: TotalLeaguesDTO })
|
||||
@@ -126,6 +129,7 @@ export class LeagueController {
|
||||
return this.leagueService.updateLeagueMemberRole({ leagueId, performerDriverId, targetDriverId, newRole: input.newRole });
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get(':leagueId/owner-summary/:ownerId')
|
||||
@ApiOperation({ summary: 'Get owner summary for a league' })
|
||||
@ApiResponse({ status: 200, description: 'League owner summary', type: LeagueOwnerSummaryDTO })
|
||||
@@ -187,6 +191,7 @@ export class LeagueController {
|
||||
};
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get(':leagueId/seasons')
|
||||
@ApiOperation({ summary: 'Get seasons for a league' })
|
||||
@ApiResponse({ status: 200, description: 'List of seasons for the league', type: [LeagueSeasonSummaryDTO] })
|
||||
@@ -195,6 +200,7 @@ export class LeagueController {
|
||||
return this.leagueService.getLeagueSeasons(query);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get(':leagueId/memberships')
|
||||
@ApiOperation({ summary: 'Get league memberships' })
|
||||
@ApiResponse({ status: 200, description: 'List of league members', type: LeagueMembershipsDTO })
|
||||
@@ -202,6 +208,7 @@ export class LeagueController {
|
||||
return this.leagueService.getLeagueMemberships(leagueId);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get(':leagueId/standings')
|
||||
@ApiOperation({ summary: 'Get league standings' })
|
||||
@ApiResponse({ status: 200, description: 'League standings', type: LeagueStandingsDTO })
|
||||
@@ -209,6 +216,7 @@ export class LeagueController {
|
||||
return this.leagueService.getLeagueStandings(leagueId);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get(':leagueId/schedule')
|
||||
@ApiOperation({ summary: 'Get league schedule' })
|
||||
@ApiResponse({ status: 200, description: 'League schedule', type: LeagueScheduleDTO })
|
||||
@@ -216,6 +224,7 @@ export class LeagueController {
|
||||
return this.leagueService.getLeagueSchedule(leagueId);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get(':leagueId/stats')
|
||||
@ApiOperation({ summary: 'Get league stats' })
|
||||
@ApiResponse({ status: 200, description: 'League stats', type: LeagueStatsDTO })
|
||||
@@ -238,6 +247,7 @@ export class LeagueController {
|
||||
return this.leagueService.createLeague(input);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get('scoring-presets')
|
||||
@ApiOperation({ summary: 'Get league scoring presets' })
|
||||
@ApiResponse({ status: 200, description: 'List of scoring presets', type: LeagueScoringPresetsDTO })
|
||||
@@ -245,6 +255,7 @@ export class LeagueController {
|
||||
return this.leagueService.listLeagueScoringPresets();
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get(':leagueId/scoring-config')
|
||||
@ApiOperation({ summary: 'Get league scoring config' })
|
||||
@ApiResponse({ status: 200, description: 'League scoring config' })
|
||||
@@ -266,6 +277,7 @@ export class LeagueController {
|
||||
return this.leagueService.transferLeagueOwnership(leagueId, body.currentOwnerId, body.newOwnerId);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get('seasons/:seasonId/sponsorships')
|
||||
@ApiOperation({ summary: 'Get season sponsorships' })
|
||||
@ApiResponse({ status: 200, description: 'Season sponsorships', type: GetSeasonSponsorshipsOutputDTO })
|
||||
@@ -273,6 +285,7 @@ export class LeagueController {
|
||||
return this.leagueService.getSeasonSponsorships(seasonId);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get(':leagueId/races')
|
||||
@ApiOperation({ summary: 'Get league races' })
|
||||
@ApiResponse({ status: 200, description: 'League races', type: GetLeagueRacesOutputDTO })
|
||||
|
||||
Reference in New Issue
Block a user