Aller au contenu

Data Flows Backend

Ce document décrit les flux de données spécifiques au backend. Voir les flux globaux pour la vue d'ensemble.

Flux de requête API

Request HTTP
┌─────────────────┐
│  Middleware     │  Helmet, CORS, Compression
└────────┬────────┘
┌─────────────────┐
│  Auth Guard     │  JWT validation
└────────┬────────┘
┌─────────────────┐
│  Interceptors   │  Logging, Transform
└────────┬────────┘
┌─────────────────┐
│  Validation     │  DTO validation (class-validator)
└────────┬────────┘
┌─────────────────┐
│  Controller     │  Route handling
└────────┬────────┘
┌─────────────────┐
│  Service        │  Business logic
└────────┬────────┘
┌─────────────────┐
│  Repository     │  Data access (TypeORM)
└────────┬────────┘
    Database

Flux de création document

// Controller
@Post()
async create(@Body() dto: CreateDocumentDto, @CurrentUser() user) {
  return this.documentsService.create(dto, user.id);
}

// Service
async create(dto: CreateDocumentDto, userId: string) {
  // 1. Valider format hash
  if (!this.hashService.isValidFormat(dto.hashDoc)) {
    throw new BadRequestException('Invalid hash format');
  }

  // 2. Vérifier unicité hash
  const existing = await this.repo.findOne({ hashDoc: dto.hashDoc });
  if (existing) {
    throw new ConflictException('Document already exists');
  }

  // 3. Upload vers S3
  const s3Key = await this.storageService.upload(dto.encryptedData);

  // 4. Créer entrée DB
  const document = this.repo.create({
    ...dto,
    ownerId: userId,
    s3Key,
  });

  // 5. Audit log
  await this.auditService.log('document.created', { documentId: document.id });

  return this.repo.save(document);
}

Flux de certification HSM

// CryptoService
async certifyDocument(documentId: string) {
  const document = await this.documentsRepo.findOne(documentId);

  // 1. Préparer données à signer
  const dataToSign = this.prepareSigningData(document);

  // 2. Signer avec HSM (PKCS#11)
  const signature = await this.hsmService.sign(dataToSign, HSM_KEY_ID);

  // 3. Obtenir horodatage TSA
  const timestamp = await this.tsaService.getTimestamp(signature);

  // 4. Créer preuve composite
  const proof = {
    hash: document.hashDoc,
    signature: signature.toString('base64'),
    timestamp: timestamp,
    keyId: HSM_KEY_ID,
    algorithm: 'RSA-PSS-SHA256',
  };

  // 5. Sauvegarder preuve
  await this.proofsRepo.save({ documentId, proof });

  return proof;
}

Flux de vérification RLS

// Interceptor appliqué globalement
@Injectable()
export class RlsInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler) {
    const request = context.switchToHttp().getRequest();
    const userId = request.user?.id;

    // Configurer RLS pour cette requête
    return from(
      this.dataSource.query(
        `SET LOCAL app.current_user_id = '${userId}'`
      )
    ).pipe(
      switchMap(() => next.handle())
    );
  }
}

Gestion des erreurs

// Exception filter global
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();

    const status = exception instanceof HttpException
      ? exception.getStatus()
      : 500;

    const message = exception instanceof HttpException
      ? exception.message
      : 'Internal server error';

    // Log sans données sensibles
    this.logger.error({
      status,
      message,
      path: ctx.getRequest().url,
      // PAS de body, params, ou données utilisateur
    });

    response.status(status).json({
      statusCode: status,
      message,
      timestamp: new Date().toISOString(),
    });
  }
}

Liens