authentication authorization
This commit is contained in:
@@ -1,9 +1,18 @@
|
||||
import 'reflect-metadata';
|
||||
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import request from 'supertest';
|
||||
import { vi } from 'vitest';
|
||||
import { TeamController } from './TeamController';
|
||||
import { TeamService } from './TeamService';
|
||||
import { CreateTeamInputDTO } from './dtos/CreateTeamInputDTO';
|
||||
import { UpdateTeamInput } from './dtos/TeamDto';
|
||||
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('TeamController', () => {
|
||||
let controller: TeamController;
|
||||
@@ -146,4 +155,75 @@ describe('TeamController', () => {
|
||||
expect(response).toEqual(result);
|
||||
});
|
||||
});
|
||||
|
||||
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: [TeamController],
|
||||
providers: [
|
||||
{
|
||||
provide: TeamService,
|
||||
useValue: {
|
||||
getAll: vi.fn(async () => ({ teams: [], totalCount: 0 })),
|
||||
getJoinRequests: vi.fn(async () => ({ requests: [], pendingCount: 0, totalCount: 0 })),
|
||||
},
|
||||
},
|
||||
],
|
||||
}).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('/teams/all').expect(200);
|
||||
});
|
||||
|
||||
it('denies non-public endpoint by default when not authenticated (401)', async () => {
|
||||
await request(app.getHttpServer()).get('/teams/t1/join-requests').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('/teams/t1/join-requests').expect(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Controller, Get, Post, Patch, Body, Req, Param, Inject } from '@nestjs/common';
|
||||
import { ApiTags, ApiResponse, ApiOperation } from '@nestjs/swagger';
|
||||
import { Public } from '../auth/Public';
|
||||
|
||||
type RequestWithUser = Record<string, unknown> & {
|
||||
user?: {
|
||||
@@ -23,6 +24,7 @@ import { GetTeamMembershipOutputDTO } from './dtos/GetTeamMembershipOutputDTO';
|
||||
export class TeamController {
|
||||
constructor(@Inject(TeamService) private readonly teamService: TeamService) {}
|
||||
|
||||
@Public()
|
||||
@Get('all')
|
||||
@ApiOperation({ summary: 'Get all teams' })
|
||||
@ApiResponse({ status: 200, description: 'List of all teams', type: GetAllTeamsOutputDTO })
|
||||
@@ -30,6 +32,7 @@ export class TeamController {
|
||||
return await this.teamService.getAll();
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get(':teamId')
|
||||
@ApiOperation({ summary: 'Get team details' })
|
||||
@ApiResponse({ status: 200, description: 'Team details', type: GetTeamDetailsOutputDTO })
|
||||
@@ -39,6 +42,7 @@ export class TeamController {
|
||||
return await this.teamService.getDetails(teamId, userId);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get(':teamId/members')
|
||||
@ApiOperation({ summary: 'Get team members' })
|
||||
@ApiResponse({ status: 200, description: 'Team members', type: GetTeamMembersOutputDTO })
|
||||
@@ -69,6 +73,7 @@ export class TeamController {
|
||||
return await this.teamService.update(teamId, input, userId);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get('driver/:driverId')
|
||||
@ApiOperation({ summary: 'Get driver\'s team' })
|
||||
@ApiResponse({ status: 200, description: 'Driver\'s team', type: GetDriverTeamOutputDTO })
|
||||
@@ -77,6 +82,7 @@ export class TeamController {
|
||||
return await this.teamService.getDriverTeam(driverId);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get(':teamId/members/:driverId')
|
||||
@ApiOperation({ summary: 'Get team membership for a driver' })
|
||||
@ApiResponse({ status: 200, description: 'Team membership', type: GetTeamMembershipOutputDTO })
|
||||
|
||||
Reference in New Issue
Block a user