feat: secure custom field value routing

This commit is contained in:
MatthieuTD
2025-09-22 10:20:49 +02:00
parent b6ca9ae54b
commit c8cc15c907
4 changed files with 274 additions and 22 deletions

View File

@@ -1,6 +1,10 @@
import { Injectable } from '@nestjs/common';
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateCustomFieldValueDto, UpdateCustomFieldValueDto } from '../shared/dto/custom-field.dto';
import {
CreateCustomFieldValueDto,
UpdateCustomFieldValueDto,
CustomFieldEntityType,
} from '../shared/dto/custom-field.dto';
@Injectable()
export class CustomFieldsService {
@@ -17,9 +21,99 @@ export class CustomFieldsService {
}
// Trouver toutes les valeurs de champs personnalisés pour une entité
async findCustomFieldValuesByEntity(entityType: string, entityId: string) {
private getCustomFieldValueKey(entityType: CustomFieldEntityType) {
switch (entityType) {
case CustomFieldEntityType.MACHINE:
return 'machineId' as const;
case CustomFieldEntityType.COMPOSANT:
return 'composantId' as const;
case CustomFieldEntityType.PIECE:
return 'pieceId' as const;
default:
throw new BadRequestException('Type d\'entité de champ personnalisé invalide.');
}
}
private async resolveEntityContext(entityType: CustomFieldEntityType, entityId: string) {
switch (entityType) {
case CustomFieldEntityType.MACHINE: {
const machine = await this.prisma.machine.findUnique({
where: { id: entityId },
select: { typeMachineId: true },
});
if (!machine) {
throw new NotFoundException('Machine introuvable.');
}
if (!machine.typeMachineId) {
throw new BadRequestException(
'La machine ne possède pas de type associé pour les champs personnalisés.',
);
}
return {
typeId: machine.typeMachineId,
customFieldTypeField: 'typeMachineId' as const,
valueKey: 'machineId' as const,
};
}
case CustomFieldEntityType.COMPOSANT: {
const composant = await this.prisma.composant.findUnique({
where: { id: entityId },
select: { typeComposantId: true },
});
if (!composant) {
throw new NotFoundException('Composant introuvable.');
}
if (!composant.typeComposantId) {
throw new BadRequestException(
'Le composant ne possède pas de type associé pour les champs personnalisés.',
);
}
return {
typeId: composant.typeComposantId,
customFieldTypeField: 'typeComposantId' as const,
valueKey: 'composantId' as const,
};
}
case CustomFieldEntityType.PIECE: {
const piece = await this.prisma.piece.findUnique({
where: { id: entityId },
select: { typePieceId: true },
});
if (!piece) {
throw new NotFoundException('Pièce introuvable.');
}
if (!piece.typePieceId) {
throw new BadRequestException(
'La pièce ne possède pas de type associé pour les champs personnalisés.',
);
}
return {
typeId: piece.typePieceId,
customFieldTypeField: 'typePieceId' as const,
valueKey: 'pieceId' as const,
};
}
default:
throw new BadRequestException('Type d\'entité de champ personnalisé invalide.');
}
}
async findCustomFieldValuesByEntity(
entityType: CustomFieldEntityType,
entityId: string,
) {
const key = this.getCustomFieldValueKey(entityType);
const whereClause = {
[entityType + 'Id']: entityId,
[key]: entityId,
};
return this.prisma.customFieldValue.findMany({
@@ -59,12 +153,35 @@ export class CustomFieldsService {
}
// Créer ou mettre à jour une valeur de champ personnalisé
async upsertCustomFieldValue(customFieldId: string, entityType: string, entityId: string, value: string) {
async upsertCustomFieldValue(
customFieldId: string,
entityType: CustomFieldEntityType,
entityId: string,
value: string,
) {
const { typeId, customFieldTypeField, valueKey } = await this.resolveEntityContext(
entityType,
entityId,
);
const allowedCustomField = await this.prisma.customField.findFirst({
where: {
id: customFieldId,
[customFieldTypeField]: typeId,
},
});
if (!allowedCustomField) {
throw new BadRequestException(
'Le champ personnalisé n\'est pas autorisé pour cette entité.',
);
}
// D'abord, essayer de trouver une valeur existante
const existingValue = await this.prisma.customFieldValue.findFirst({
where: {
customFieldId,
[entityType + 'Id']: entityId,
[valueKey]: entityId,
},
});
@@ -83,7 +200,7 @@ export class CustomFieldsService {
data: {
customFieldId,
value,
[entityType + 'Id']: entityId,
[valueKey]: entityId,
},
include: {
customField: true,
@@ -91,4 +208,4 @@ export class CustomFieldsService {
});
}
}
}
}