mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-11-01 01:21:18 +01: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,7 +136,12 @@ 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 =
 | 
			
		||||
@ -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…
	
		Reference in New Issue
	
	Block a user