Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions apps/onboarding-ms/src/main/docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2192,10 +2192,7 @@
"description" : "OK",
"content" : {
"application/octet-stream" : {
"schema" : {
"format" : "binary",
"type" : "string"
}
"schema" : { }
}
}
},
Expand Down
4 changes: 1 addition & 3 deletions apps/onboarding-ms/src/main/docs/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1616,9 +1616,7 @@ paths:
description: OK
content:
application/octet-stream:
schema:
format: binary
type: string
schema: {}
"401":
description: Not Authorized
"403":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public Uni<RestResponse<File>> getContract(@PathParam(value = "onboardingId") St
@GET
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Path("/{onboardingId}/contract-signed")
public Uni<RestResponse<File>> getContractSigned(@PathParam(value = "onboardingId") String onboardingId) {
public Uni<RestResponse<Object>> getContractSigned(@PathParam(value = "onboardingId") String onboardingId) {
return tokenService.retrieveSignedFile(onboardingId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public interface TokenService {

Uni<RestResponse<File>> retrieveContract(String onboardingId, boolean isSigned);

Uni<RestResponse<File>> retrieveSignedFile(String onboardingId);
Uni<RestResponse<Object>> retrieveSignedFile(String onboardingId);

Uni<RestResponse<File>> retrieveTemplateAttachment(String onboardingId, String attachmentName);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.StreamingOutput;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.bson.Document;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.resteasy.reactive.RestResponse;
Expand Down Expand Up @@ -90,13 +90,35 @@ public static void isP7mValid(File contract, SignatureService signatureService)
signatureService.verifySignature(contract);
}

public static void isPdfValid(File contract) {
try (PDDocument document = Loader.loadPDF(contract)) {
document.getNumberOfPages();
PDFTextStripper stripper = new PDFTextStripper();
stripper.getText(document);
public static File checkAndRepairPdf(File file) {
try (PDDocument doc = Loader.loadPDF(file)) {

// 1. Validazione base (quella che facevi prima)
if (doc.getNumberOfPages() == 0) {
throw new InvalidRequestException(ORIGINAL_DOCUMENT_NOT_FOUND.getMessage(), ORIGINAL_DOCUMENT_NOT_FOUND.getCode());
}

// 2. Logica di Riparazione (Sanitizzazione)
// Creiamo un temp file dove risalvare il PDF pulito con permessi più restrittivi
File repairedFile = Files.createTempFile("repaired_", ".pdf").toFile();

// Rimuoviamo sicurezze che potrebbero dar fastidio al browser
doc.setAllSecurityToBeRemoved(true);

// Il salvataggio riscrive il file correggendo gli offset corrotti (fix per "Spett.le")
doc.save(repairedFile);

return repairedFile;

} catch (IOException e) {
throw new InvalidRequestException(ORIGINAL_DOCUMENT_NOT_FOUND.getMessage(), ORIGINAL_DOCUMENT_NOT_FOUND.getCode());
// Se il PDF è talmente rotto da non aprirsi, o se il salvataggio fallisce:
// Logghiamo l'errore ma restituiamo il file originale come fallback.
// In questo modo non blocchi l'utente, ma tenti il "best effort".
log.warn("Impossibile riparare il PDF, restituisco l'originale. Errore: " + e.getMessage());
return file;

// OPPURE: Se preferisci essere severo e bloccare tutto se il PDF è brutto:
// throw new InvalidRequestException("PDF Corrotto", "Error");
}
}

Expand Down Expand Up @@ -130,23 +152,65 @@ public Uni<RestResponse<File>> retrieveContract(String onboardingId, boolean isS
}

@Override
public Uni<RestResponse<File>> retrieveSignedFile(String onboardingId) {
public Uni<RestResponse<Object>> retrieveSignedFile(String onboardingId) {
return Token.findById(onboardingId)
.map(Token.class::cast)
.onItem().transformToUni(token -> Uni.createFrom().item(() -> azureBlobClient.retrieveFile(token.getContractSigned()))
.runSubscriptionOn(Executors.newSingleThreadExecutor())
.onItem().transform(contract -> {
if (token.getContractSigned().endsWith(".pdf")) {
isPdfValid(contract);
} else {

List<File> tempFiles = new ArrayList<>();
tempFiles.add(contract);

File fileToSend = contract;

// 1. Gestione P7M: Estrazione
if (!token.getContractSigned().endsWith(".pdf")) {
// Validiamo la firma (importante per la sicurezza)
isP7mValid(contract, signatureService);
File original = signatureService.extractFile(contract);
isPdfValid(original);
// Estraiamo il contenuto
fileToSend = signatureService.extractFile(contract);
tempFiles.add(fileToSend);
}
RestResponse.ResponseBuilder<File> response = RestResponse.ResponseBuilder.ok(contract, MediaType.APPLICATION_OCTET_STREAM);
response.header(HTTP_HEADER_CONTENT_DISPOSITION, HTTP_HEADER_VALUE_ATTACHMENT_FILENAME + getCurrentContractName(token, true));
return response.build();
}).onFailure().recoverWithUni(() -> Uni.createFrom().item(RestResponse.ResponseBuilder.<File>notFound().build())));

// 2. Validazione e Riparazione PDF
// Questo metodo si occupa di caricare, controllare e sanitizzare il file.
File repairedFile = checkAndRepairPdf(fileToSend);
if (repairedFile != fileToSend) {
tempFiles.add(repairedFile);
fileToSend = repairedFile;
}

final File finalFileToSend = fileToSend;

// 3. Creazione dello StreamingOutput per invio e pulizia
StreamingOutput streamingOutput = output -> {
try {
Files.copy(finalFileToSend.toPath(), output);
output.flush();
} finally {
// Pulizia: elimina tutti i file temporanei tracciati
tempFiles.forEach(f -> {
try {
Files.deleteIfExists(f.toPath());
} catch (IOException e) {
log.warn("Errore durante la cancellazione del file temporaneo: {}", f.getAbsolutePath());
}
});
}
};

// 4. Costruzione Risposta
String filename = getCurrentContractName(token, true);
if (filename.endsWith(".p7m")) {
filename = filename.replace(".p7m", ".pdf");
}

return RestResponse.ResponseBuilder.ok((Object) streamingOutput, MediaType.APPLICATION_OCTET_STREAM)
.header(HTTP_HEADER_CONTENT_DISPOSITION, HTTP_HEADER_VALUE_ATTACHMENT_FILENAME + filename)
.build();

}).onFailure().recoverWithUni(() -> Uni.createFrom().item(RestResponse.ResponseBuilder.notFound().build())));
}

private String getContractNotSigned(String onboardingId, Token token) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ void getContract() {
void getContractSignedTest() {
// given
final String onboardingId = "onboardingId";
RestResponse.ResponseBuilder<File> response = RestResponse.ResponseBuilder.ok();
RestResponse.ResponseBuilder<Object> response = RestResponse.ResponseBuilder.ok();

when(tokenService.retrieveSignedFile(onboardingId))
.thenReturn(Uni.createFrom().item(response.build()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,14 @@ void retrieveContractSignedKOTest() {
when(azureBlobClient.retrieveFile(anyString())).thenReturn(new File(resourcePath));

// when
UniAssertSubscriber<RestResponse<File>> subscriber =
UniAssertSubscriber<RestResponse<Object>> subscriber =
tokenService
.retrieveSignedFile(onboardingId)
.subscribe()
.withSubscriber(UniAssertSubscriber.create());

// then
RestResponse<File> actual = subscriber.awaitItem().getItem();
RestResponse<Object> actual = subscriber.awaitItem().getItem();
assertNotNull(actual);
assertEquals(RestResponse.Status.NOT_FOUND.getStatusCode(), actual.getStatus());
}
Expand All @@ -186,14 +186,14 @@ void retrieveContractSignedPdfOKTest() {
when(azureBlobClient.retrieveFile(anyString())).thenReturn(new File(resourcePath));

// when
UniAssertSubscriber<RestResponse<File>> subscriber =
UniAssertSubscriber<RestResponse<Object>> subscriber =
tokenService
.retrieveSignedFile(onboardingId)
.subscribe()
.withSubscriber(UniAssertSubscriber.create());

// then
RestResponse<File> actual = subscriber.awaitItem().getItem();
RestResponse<Object> actual = subscriber.awaitItem().getItem();
assertNotNull(actual);
assertEquals(RestResponse.Status.OK.getStatusCode(), actual.getStatus());
}
Expand All @@ -218,14 +218,14 @@ void retrieveContractSignedP7mOKTest() {
when(signatureService.extractFile(any())).thenReturn(new File(resourceExtractedPath));

// when
UniAssertSubscriber<RestResponse<File>> subscriber =
UniAssertSubscriber<RestResponse<Object>> subscriber =
tokenService
.retrieveSignedFile(onboardingId)
.subscribe()
.withSubscriber(UniAssertSubscriber.create());

// then
RestResponse<File> actual = subscriber.awaitItem().getItem();
RestResponse<Object> actual = subscriber.awaitItem().getItem();
assertNotNull(actual);
assertEquals(RestResponse.Status.OK.getStatusCode(), actual.getStatus());
}
Expand Down
Loading