added middleware
This commit is contained in:
parent
f3d0659c39
commit
a5f76f01ad
|
|
@ -1,8 +1,9 @@
|
|||
import { Controller, Get, Post, Patch, Delete, Param, Body, UsePipes, ValidationPipe, Query, ParseUUIDPipe, HttpCode, HttpStatus } from '@nestjs/common';
|
||||
import { Controller, Get, Post, Patch, Delete, Param, Body, UsePipes, ValidationPipe, Query, ParseUUIDPipe, HttpCode, HttpStatus, Request } from '@nestjs/common';
|
||||
import { AnswerService } from './answer.service';
|
||||
import { CreateAnswerDto } from './dto/create-answer.dto';
|
||||
import { UpdateAnswerDto } from './dto/update-answer.dto';
|
||||
import { Answer } from './entity/answer.entity';
|
||||
import { AuthRequest } from '../middleware/jwtMiddleware';
|
||||
|
||||
@Controller('answers')
|
||||
export class AnswerController {
|
||||
|
|
@ -10,8 +11,8 @@ export class AnswerController {
|
|||
|
||||
@Post()
|
||||
@UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
|
||||
async create(@Body() body: CreateAnswerDto): Promise<Answer> {
|
||||
return this.answerService.create(body);
|
||||
async create(@Body() body: CreateAnswerDto, @Request() req: AuthRequest): Promise<Answer> {
|
||||
return this.answerService.create(body, req.user);
|
||||
}
|
||||
|
||||
@Get('findAll')
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common';
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Answer } from './entity/answer.entity';
|
||||
import { CreateAnswerDto } from './dto/create-answer.dto';
|
||||
import { UpdateAnswerDto } from './dto/update-answer.dto';
|
||||
import { CreateAttachmentDto } from '../attachment/dto/create_attachment.dto';
|
||||
import { Attachment } from '../attachment/entity/attachment.entity';
|
||||
import { AuthRequest } from '../middleware/jwtMiddleware';
|
||||
|
||||
@Injectable()
|
||||
export class AnswerService {
|
||||
|
|
@ -14,18 +13,15 @@ export class AnswerService {
|
|||
private readonly answerRepository: Repository<Answer>,
|
||||
) {}
|
||||
|
||||
async create(data: CreateAnswerDto, token: any): Promise<Answer> {
|
||||
if (!token || !token.sub) {
|
||||
throw new InternalServerErrorException('Failed to extract sub from token');
|
||||
}
|
||||
async create(data: CreateAnswerDto, user: AuthRequest['user']): Promise<Answer> {
|
||||
const answer = this.answerRepository.create({
|
||||
...data,
|
||||
participantId: token.sub, // token.sub is userId, but we want participantId ?!
|
||||
participantId: user.sub,
|
||||
});
|
||||
return await this.answerRepository.save(answer);
|
||||
}
|
||||
|
||||
async findAll( page = 1, limit = 10 ): Promise<{
|
||||
async findAll(page = 1, limit = 10): Promise<{
|
||||
docs: Answer[];
|
||||
totalDocs: number;
|
||||
limit: number;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { Controller, Get, Post, Patch, Delete, Param, Body, UsePipes, ValidationPipe, Query, ParseUUIDPipe, HttpCode, HttpStatus } from '@nestjs/common';
|
||||
import { Controller, Get, Post, Patch, Delete, Param, Body, UsePipes, ValidationPipe, Query, ParseUUIDPipe, HttpCode, HttpStatus, Request } from '@nestjs/common';
|
||||
import { AttachmentService } from './attachment.service';
|
||||
import { CreateAttachmentDto } from './dto/create_attachment.dto';
|
||||
import { UpdateAttachmentDto } from './dto/update_attachment.dto';
|
||||
import { Attachment } from './entity/attachment.entity';
|
||||
import { AuthRequest } from '../middleware/jwtMiddleware';
|
||||
|
||||
@Controller('attachments')
|
||||
export class AttachmentController {
|
||||
|
|
@ -10,8 +11,8 @@ export class AttachmentController {
|
|||
|
||||
@Post()
|
||||
@UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
|
||||
async create(@Body() body: CreateAttachmentDto): Promise<Attachment> {
|
||||
return this.attachmentService.create(body);
|
||||
async create(@Body() body: CreateAttachmentDto, @Request() req: AuthRequest): Promise<Attachment> {
|
||||
return this.attachmentService.create(body, req.user);
|
||||
}
|
||||
|
||||
@Get('findAll')
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common';
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Attachment } from './entity/attachment.entity';
|
||||
import { CreateAttachmentDto } from './dto/create_attachment.dto';
|
||||
import { UpdateAttachmentDto } from './dto/update_attachment.dto';
|
||||
import axios from 'axios';
|
||||
import { decode } from 'jsonwebtoken';
|
||||
import { AuthRequest } from '../middleware/jwtMiddleware';
|
||||
|
||||
@Injectable()
|
||||
export class AttachmentService {
|
||||
|
|
@ -14,15 +13,10 @@ export class AttachmentService {
|
|||
private readonly attachmentRepository: Repository<Attachment>,
|
||||
) {}
|
||||
|
||||
async create(data: CreateAttachmentDto, token: any): Promise<Attachment> {
|
||||
// Extract sub from decoded token
|
||||
if (!token || !token.sub) {
|
||||
throw new InternalServerErrorException('Failed to extract sub from token');
|
||||
}
|
||||
// Create the attachment with ownerId set to token.sub
|
||||
async create(data: CreateAttachmentDto, user: AuthRequest['user']): Promise<Attachment> {
|
||||
const attachment = this.attachmentRepository.create({
|
||||
...data,
|
||||
ownerId: token.sub, // Set ownerId to the token's sub
|
||||
ownerId: user.sub,
|
||||
});
|
||||
return await this.attachmentRepository.save(attachment);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export class CreateAttachmentDto extends BaseDto {
|
|||
@IsUrl()
|
||||
storageUrl: string;
|
||||
|
||||
@IsUUID('4')
|
||||
ownerId: string;
|
||||
// @IsUUID('4')
|
||||
// @IsOptional()
|
||||
// ownerId?: string;
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ export class FormResultController {
|
|||
}
|
||||
|
||||
@Get(':id/refresh')
|
||||
async getFormStatistics( // updates data in database and then returns
|
||||
async getFormStatistics( // updates data in database and then returns it
|
||||
@Param('formId', new ParseUUIDPipe()) formId: string,
|
||||
): Promise<FormResult> {
|
||||
return this.formResultService.getFormStatistics(formId);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { JwtMiddleware } from './middleware/jwtMiddleware';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
app.enableCors({ origin: 'http://localhost:3001' });
|
||||
app.use(new JwtMiddleware().use);
|
||||
await app.listen(process.env.PORT ?? 3000);
|
||||
}
|
||||
bootstrap();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import { Injectable, NestMiddleware, UnauthorizedException } from '@nestjs/common';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import * as jwt from 'jsonwebtoken';
|
||||
import { validate as isValidUUID } from 'uuid';
|
||||
|
||||
@Injectable()
|
||||
export class JwtMiddleware implements NestMiddleware {
|
||||
use(req: Request, res: Response, next: NextFunction) {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
throw new UnauthorizedException('Missing or invalid Authorization header');
|
||||
}
|
||||
|
||||
const token = authHeader.split(' ')[1];
|
||||
try {
|
||||
const decoded = jwt.decode(token) as any;
|
||||
if (!decoded || !decoded.sub) {
|
||||
throw new UnauthorizedException('Invalid token: missing sub');
|
||||
}
|
||||
if (!isValidUUID(decoded.sub)) {
|
||||
throw new UnauthorizedException('Invalid token: sub is not a valid UUIDv4');
|
||||
}
|
||||
const { sub, realm_access, resource_access } = decoded;
|
||||
const roles = [ // extract the role you wanna use in guards
|
||||
...(realm_access?.roles || []),
|
||||
// ...(resource_access?.account?.roles || []),
|
||||
];
|
||||
req['user'] = { sub, roles };
|
||||
next();
|
||||
} catch (error) {
|
||||
throw new UnauthorizedException('Invalid token');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface AuthRequest extends Request {
|
||||
user: { sub: string; roles: string[] };
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
import { Controller, Get, Post, Patch, Delete, Param, Body, UsePipes, ValidationPipe, Query } from '@nestjs/common';
|
||||
import { Controller, Get, Post, Patch, Delete, Param, Body, UsePipes, ValidationPipe, Query, Request } from '@nestjs/common';
|
||||
import { ParticipantService } from './participant.service';
|
||||
import { CreateParticipantDto } from './dto/create-participant.dto';
|
||||
import { UpdateParticipantDto } from './dto/update-participant.dto';
|
||||
import { Participant } from './entity/participant.entity';
|
||||
import { AuthRequest } from '../middleware/jwtMiddleware';
|
||||
|
||||
@Controller('participants')
|
||||
export class ParticipantController {
|
||||
|
|
@ -10,8 +11,8 @@ export class ParticipantController {
|
|||
|
||||
@Post()
|
||||
@UsePipes(new ValidationPipe({ transform: true }))
|
||||
async create(@Body() body: CreateParticipantDto): Promise<Participant> {
|
||||
return this.participantService.create(body);
|
||||
async create(@Body() body: CreateParticipantDto, @Request() req: AuthRequest): Promise<Participant> {
|
||||
return this.participantService.create(body, req.user);
|
||||
}
|
||||
|
||||
@Get('findAll')
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { Injectable, InternalServerErrorException } from '@nestjs/common';
|
||||
import { Injectable, InternalServerErrorException, ConflictException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { CreateParticipantDto } from './dto/create-participant.dto';
|
||||
import { Participant } from './entity/participant.entity';
|
||||
import { UpdateParticipantDto } from './dto/update-participant.dto';
|
||||
import axios from 'axios';
|
||||
import { decode } from 'jsonwebtoken';
|
||||
import { AuthRequest } from '../middleware/jwtMiddleware';
|
||||
|
||||
@Injectable()
|
||||
export class ParticipantService {
|
||||
|
|
@ -14,51 +13,24 @@ export class ParticipantService {
|
|||
private readonly participantRepository: Repository<Participant>,
|
||||
) {}
|
||||
|
||||
async create(data: CreateParticipantDto): Promise<Participant> {
|
||||
async create(data: CreateParticipantDto, user: AuthRequest['user']): Promise<Participant> {
|
||||
try {
|
||||
// Here we send a sample request, in final implementation, there'll be no need for that, we'll get the decoded token
|
||||
const authResponse = await axios.post(
|
||||
'https://auth.didvan.com/realms/didvan/protocol/openid-connect/token',
|
||||
new URLSearchParams({
|
||||
client_id: 'didvan-app',
|
||||
username: 'bob',
|
||||
password: 'developer_password',
|
||||
grant_type: 'password',
|
||||
}),
|
||||
{
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// Decode the access token to extract the 'sub' claim
|
||||
const token = authResponse.data.access_token;
|
||||
const decodedToken: any = decode(token);
|
||||
if (!decodedToken || !decodedToken.sub) {
|
||||
throw new InternalServerErrorException('Failed to decode token or extract sub');
|
||||
}
|
||||
|
||||
// Check if participant with this sub already exists
|
||||
const existingParticipant = await this.participantRepository.findOne({ where: { id: decodedToken.sub } });
|
||||
const existingParticipant = await this.participantRepository.findOne({ where: { userId: user.sub } });
|
||||
if (existingParticipant) {
|
||||
throw new InternalServerErrorException(`Participant with ID ${decodedToken.sub} already exists`);
|
||||
throw new ConflictException(`Participant with userId ${user.sub} already exists`);
|
||||
}
|
||||
|
||||
// Create the participant with the 'sub' as the ID
|
||||
const participant = this.participantRepository.create({
|
||||
...data,
|
||||
// id: , let it assign a random id
|
||||
userId: decodedToken.sub, // Set userId to the same sub
|
||||
metadata: data.metadata
|
||||
? { entries: data.metadata.entries }
|
||||
: { entries: [] },
|
||||
status: data.status || 'active',
|
||||
userId: user.sub,
|
||||
role: data.role || 'user',
|
||||
});
|
||||
|
||||
// Save the participant to the database
|
||||
return await this.participantRepository.save(participant);
|
||||
} catch (error) {
|
||||
if (error instanceof ConflictException) {
|
||||
throw error;
|
||||
}
|
||||
throw new InternalServerErrorException(`Failed to create participant: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue