mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-05-24 02:42:02 +00:00
Fix cert-sign API NullPointerException when pageNumber is omitted for invisible signatures (#3463)
# Description of Changes Please provide a summary of the changes, including: - **What was changed** - Updated `SignPDFWithCertRequest` to use `Boolean` for `showSignature` and `showLogo`, and made `pageNumber` nullable. - In `CertSignController`: - Added an `@InitBinder` to convert empty multipart fields to `null`. - Extended `@PostMapping` to consume both `multipart/form-data` and `application/x-www-form-urlencoded`. - Wrapped `pageNumber` calculation in a null-check (`pageNumber = request.getPageNumber() != null ? request.getPageNumber() - 1 : null`). - Changed signature-visualization and logo checks to `Boolean.TRUE.equals(...)` to avoid unboxing NPE. - Cleaned up imports and schema annotations in the request model. - **Why the change was made** - Prevent a 500 Internal Server Error caused by calling `.intValue()` on a null `pageNumber` when `showSignature=false` (invisible signatures). - Ensure that omitting `pageNumber` doesn’t break clients using the “try it out” swagger UI or `curl`-based requests. - **Any challenges encountered** - Configuring Spring’s data binder to treat empty file inputs as `null` required a custom `PropertyEditorSupport`. - Balancing backward compatibility with stricter type handling (switching from primitive `boolean` to boxed `Boolean`). Closes #3459 --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details.
This commit is contained in:
parent
e2a5874a88
commit
2ac606608a
@ -1,6 +1,7 @@
|
||||
package stirling.software.SPDF.controller.api.security;
|
||||
|
||||
import java.awt.*;
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.security.*;
|
||||
@ -53,7 +54,10 @@ import org.bouncycastle.operator.OperatorCreationException;
|
||||
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
|
||||
import org.bouncycastle.pkcs.PKCSException;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@ -82,6 +86,18 @@ public class CertSignController {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
@InitBinder
|
||||
public void initBinder(WebDataBinder binder) {
|
||||
binder.registerCustomEditor(
|
||||
MultipartFile.class,
|
||||
new PropertyEditorSupport() {
|
||||
@Override
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
setValue(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private final CustomPDFDocumentFactory pdfDocumentFactory;
|
||||
|
||||
private static void sign(
|
||||
@ -103,8 +119,7 @@ public class CertSignController {
|
||||
signature.setLocation(location);
|
||||
signature.setReason(reason);
|
||||
signature.setSignDate(Calendar.getInstance());
|
||||
|
||||
if (showSignature) {
|
||||
if (Boolean.TRUE.equals(showSignature)) {
|
||||
SignatureOptions signatureOptions = new SignatureOptions();
|
||||
signatureOptions.setVisualSignature(
|
||||
instance.createVisibleSignature(doc, signature, pageNumber, showLogo));
|
||||
@ -121,13 +136,18 @@ public class CertSignController {
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping(consumes = "multipart/form-data", value = "/cert-sign")
|
||||
@PostMapping(
|
||||
consumes = {
|
||||
MediaType.MULTIPART_FORM_DATA_VALUE,
|
||||
MediaType.APPLICATION_FORM_URLENCODED_VALUE
|
||||
},
|
||||
value = "/cert-sign")
|
||||
@Operation(
|
||||
summary = "Sign PDF with a Digital Certificate",
|
||||
description =
|
||||
"This endpoint accepts a PDF file, a digital certificate and related"
|
||||
+ " information to sign the PDF. It then returns the digitally signed PDF"
|
||||
+ " file. Input:PDF Output:PDF Type:SISO")
|
||||
+ " information to sign the PDF. It then returns the digitally signed PDF"
|
||||
+ " file. Input:PDF Output:PDF Type:SISO")
|
||||
public ResponseEntity<byte[]> signPDFWithCert(@ModelAttribute SignPDFWithCertRequest request)
|
||||
throws Exception {
|
||||
MultipartFile pdf = request.getFileInput();
|
||||
@ -137,12 +157,13 @@ public class CertSignController {
|
||||
MultipartFile p12File = request.getP12File();
|
||||
MultipartFile jksfile = request.getJksFile();
|
||||
String password = request.getPassword();
|
||||
Boolean showSignature = request.isShowSignature();
|
||||
Boolean showSignature = request.getShowSignature();
|
||||
String reason = request.getReason();
|
||||
String location = request.getLocation();
|
||||
String name = request.getName();
|
||||
Integer pageNumber = request.getPageNumber() - 1;
|
||||
Boolean showLogo = request.isShowLogo();
|
||||
// Convert 1-indexed page number (user input) to 0-indexed page number (API requirement)
|
||||
Integer pageNumber = request.getPageNumber() != null ? (request.getPageNumber() - 1) : null;
|
||||
Boolean showLogo = request.getShowLogo();
|
||||
|
||||
if (certType == null) {
|
||||
throw new IllegalArgumentException("Cert type must be provided");
|
||||
@ -279,7 +300,7 @@ public class CertSignController {
|
||||
widget.setAppearance(appearance);
|
||||
|
||||
try (PDPageContentStream cs = new PDPageContentStream(doc, appearanceStream)) {
|
||||
if (showLogo) {
|
||||
if (Boolean.TRUE.equals(showLogo)) {
|
||||
cs.saveGraphicsState();
|
||||
PDExtendedGraphicsState extState = new PDExtendedGraphicsState();
|
||||
extState.setBlendMode(BlendMode.MULTIPLY);
|
||||
|
@ -20,7 +20,8 @@ public class SignPDFWithCertRequest extends PDFFile {
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"The private key for the digital certificate (required for PEM type certificates)")
|
||||
"The private key for the digital certificate (required for PEM type"
|
||||
+ " certificates)")
|
||||
private MultipartFile privateKeyFile;
|
||||
|
||||
@Schema(description = "The digital certificate (required for PEM type certificates)")
|
||||
@ -32,11 +33,11 @@ public class SignPDFWithCertRequest extends PDFFile {
|
||||
@Schema(description = "The JKS keystore file (Java Key Store)")
|
||||
private MultipartFile jksFile;
|
||||
|
||||
@Schema(description = "The password for the keystore or the private key")
|
||||
@Schema(description = "The password for the keystore or the private key", format = "password")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "Whether to visually show the signature in the PDF file")
|
||||
private boolean showSignature;
|
||||
private Boolean showSignature;
|
||||
|
||||
@Schema(description = "The reason for signing the PDF")
|
||||
private String reason;
|
||||
@ -49,9 +50,10 @@ public class SignPDFWithCertRequest extends PDFFile {
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"The page number where the signature should be visible. This is required if showSignature is set to true")
|
||||
"The page number where the signature should be visible. This is required if"
|
||||
+ " showSignature is set to true")
|
||||
private Integer pageNumber;
|
||||
|
||||
@Schema(description = "Whether to visually show a signature logo along with the signature")
|
||||
private boolean showLogo;
|
||||
private Boolean showLogo;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user