aboutsummaryrefslogtreecommitdiff
path: root/pdf-as-web/src/test/java
diff options
context:
space:
mode:
authorJakob Heher <jakob.heher@iaik.tugraz.at>2026-04-15 13:49:22 +0200
committerGitHub <noreply@github.com>2026-04-15 13:49:22 +0200
commit77dd3fcc4d85088b15ab859c4438521d9cd6ed10 (patch)
treeaefedc8d2ef77e6819b46a948459d6016dfe5b62 /pdf-as-web/src/test/java
parent88930540361a88ff56e07fed31004b583f2e729f (diff)
downloadpdf-as-4-77dd3fcc4d85088b15ab859c4438521d9cd6ed10.tar.gz
pdf-as-4-77dd3fcc4d85088b15ab859c4438521d9cd6ed10.tar.bz2
pdf-as-4-77dd3fcc4d85088b15ab859c4438521d9cd6ed10.zip
pdf-as-5 (#82)
- JDK 17 - PDFBox 3 - PDF-AS Web moved to Spring Boot - MOA Integration tests w/ new error code --------- Co-authored-by: Gerald Palfinger <gerald.palfinger@a-sit.at> Co-authored-by: kathrin.resek <kathrin.resek@a-sit.at>
Diffstat (limited to 'pdf-as-web/src/test/java')
-rw-r--r--pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/JsonApiTest.java130
-rw-r--r--pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/MockMoaSigningTest.java267
-rw-r--r--pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/SimpleSignServletTest.java10
-rw-r--r--pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/SimpleVerifyServletTest.java10
-rw-r--r--pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/TestUtils.java30
5 files changed, 437 insertions, 10 deletions
diff --git a/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/JsonApiTest.java b/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/JsonApiTest.java
new file mode 100644
index 00000000..71761e1d
--- /dev/null
+++ b/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/JsonApiTest.java
@@ -0,0 +1,130 @@
+package at.gv.egiz.pdfas.web.test;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.junit.Assert.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.jayway.jsonpath.JsonPath;
+import lombok.Lombok;
+import lombok.SneakyThrows;
+import lombok.val;
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Map;
+import java.util.UUID;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(properties = {
+ "management.endpoint.metrics.enabled=true",
+ "management.endpoints.web.exposure.include=metrics"
+})
+@AutoConfigureMockMvc
+public class JsonApiTest {
+ @Autowired MockMvc mvc;
+ @Autowired ObjectMapper om;
+
+ static {
+ try {
+ System.setProperty("pdf-as-web.conf",
+ (new File(".").getCanonicalPath()) + "/src/test/resources/config/pdfas/pdf-as-web.properties");
+ } catch (Throwable t) {
+ throw Lombok.sneakyThrow(t);
+ }
+ }
+
+ @Test
+ @SneakyThrows
+ public void sign_single_jks() {
+ try (val watcher = TestUtils.OperationCountWatcher(mvc, "operation:sign", "status:ok")) {
+ final String pdf = Base64.getEncoder().encodeToString(
+ IOUtils.toByteArray(JsonApiTest.class.getResourceAsStream("/data/enc_own.pdf")));
+
+ final String signRequestID = UUID.randomUUID().toString();
+ final String signRequest = om.writeValueAsString(
+ Map.of(
+ "requestID", signRequestID,
+ "inputData", pdf,
+ "parameters", Map.of(
+ "connector", "jks",
+ "transactionId", UUID.randomUUID().toString()
+ )
+ )
+ );
+
+ final String signResponse = mvc.perform(
+ post("/api/v2/sign/single")
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON)
+ .content(signRequest)
+ )
+ .andExpect(status().isOk())
+ .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
+ .andExpect(jsonPath("$.requestID").value(signRequestID))
+ .andExpect(jsonPath("$.signedPDF").isNotEmpty())
+ .andExpect(jsonPath("$.verificationResponse").exists())
+ .andReturn().getResponse().getContentAsString();
+
+ final byte[] signedPDF = Base64.getDecoder().decode(JsonPath.<String>read(signResponse, "$.signedPDF"));
+ assertArrayEquals("Signed data looks PDF-ish (%PDF- header)",
+ new byte[]{'%', 'P', 'D', 'F', '-'}, Arrays.copyOfRange(signedPDF, 0, 5));
+ }
+ }
+
+ @Test
+ @SneakyThrows
+ public void verify_single() {
+ try (val watcher = TestUtils.OperationCountWatcher(mvc, "operation:verify", "status:ok")) {
+ final String pdf = Base64.getEncoder().encodeToString(
+ IOUtils.toByteArray(JsonApiTest.class.getResourceAsStream("/data/dummy-pdf-signed.pdf")));
+
+ final String verifyRequestID = UUID.randomUUID().toString();
+ final String verifyRequest = om.writeValueAsString(
+ Map.of(
+ "requestID", verifyRequestID,
+ "inputData", pdf,
+ "verificationLevel", "intOnly"
+ )
+ );
+
+ mvc.perform(
+ post("/api/v2/verify")
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON)
+ .content(verifyRequest)
+ )
+ .andExpect(status().isOk())
+ .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
+ .andExpect(jsonPath("$.verifyResults").isArray())
+ .andExpect(jsonPath("$.verifyResults.length()").value(1))
+ .andExpect(jsonPath("$.verifyResults[0].requestID").value(verifyRequestID))
+ .andExpect(jsonPath("$.verifyResults[0].error").isEmpty())
+ .andExpect(jsonPath("$.verifyResults[0].signatureIndex").value(0))
+ .andExpect(jsonPath("$.verifyResults[0].signedBy").value("CN=MOA-ID IDP (Test-Version),O=EGIZ,L=Graz,C=AT"));
+ }
+ }
+
+ @Test
+ @SneakyThrows
+ public void openapi_docs_test() {
+ mvc.perform(get("/v3/api-docs"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.paths['/api/v2/sign/single']").exists())
+ .andExpect(jsonPath("$.paths['/api/v2/sign/bulk']").exists())
+ .andExpect(jsonPath("$.paths['/api/v2/sign/multiple']").exists())
+ .andExpect(jsonPath("$.paths['/api/v2/sign/multiple/get-result']").exists())
+ .andExpect(jsonPath("$.paths['/api/v2/verify']").exists());
+ }
+}
diff --git a/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/MockMoaSigningTest.java b/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/MockMoaSigningTest.java
new file mode 100644
index 00000000..466cfcca
--- /dev/null
+++ b/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/MockMoaSigningTest.java
@@ -0,0 +1,267 @@
+package at.gv.egiz.pdfas.web.test;
+
+import at.gv.e_government.reference.namespace.moa._20020822_.*;
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+import at.gv.egiz.pdfas.lib.api.Configuration;
+import at.gv.egiz.pdfas.lib.api.IConfigurationConstants;
+import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner;
+import at.gv.egiz.pdfas.lib.impl.configuration.ConfigurationImpl;
+import at.gv.egiz.pdfas.moa.MOAConnector;
+import at.gv.egiz.pdfas.sigs.pades.PAdESSignerKeystore;
+import at.gv.egiz.pdfas.sigs.pkcs7detached.PKCS7DetachedSigner;
+import at.gv.egiz.pdfas.web.config.WebConfiguration;
+import at.gv.egiz.pdfas.web.helper.PdfAsHelper;
+import at.gv.egiz.pdfas.web.servlets.ExternSignServlet;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.jayway.jsonpath.JsonPath;
+import iaik.x509.X509Certificate;
+import jakarta.jws.WebService;
+import jakarta.xml.ws.Endpoint;
+import lombok.Lombok;
+import lombok.SneakyThrows;
+import lombok.val;
+import org.apache.commons.io.IOUtils;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.event.annotation.BeforeTestClass;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+
+import java.io.*;
+import java.net.ServerSocket;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.util.*;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(properties = {
+ "management.endpoint.metrics.enabled=true",
+ "management.endpoints.web.exposure.include=metrics"
+})
+@AutoConfigureMockMvc
+public class MockMoaSigningTest {
+ @Autowired MockMvc mvc;
+ @Autowired ObjectMapper om;
+
+ static {
+ try {
+ System.setProperty("pdf-as-web.conf",
+ (new File(".").getCanonicalPath()) + "/src/test/resources/config/pdfas/pdf-as-web.properties");
+ } catch (Throwable t) {
+ throw Lombok.sneakyThrow(t);
+ }
+ }
+
+ @BeforeClass
+ public static void jceWorkaround() {
+ System.setProperty("javax.net.ssl.trustStoreType", "JKS");
+ }
+
+ @WebService(
+ serviceName = "SignatureCreationService",
+ portName = "SignatureCreationPort",
+ targetNamespace = "http://reference.e-government.gv.at/namespace/moa/20020822#",
+ endpointInterface =
+ "at.gv.e_government.reference.namespace.moa._20020822_.SignatureCreationPortType")
+ static class MockMoa implements AutoCloseable, SignatureCreationPortType {
+ @SneakyThrows
+ private static int freePort() {
+ try (ServerSocket socket = new ServerSocket(0)) {
+ return socket.getLocalPort();
+ }
+ }
+ private static String azstring(int length) {
+ return
+ new Random().ints(97,123).limit(length)
+ .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
+ .toString();
+ }
+ public final int port = freePort();
+ public final String endpointURL = "http://127.0.0.1:"+port+"/moa-spss/services/SignatureCreation";
+ public final Endpoint endpoint =
+ Endpoint.publish(endpointURL, this);
+ public final String keyIdentifier = azstring(16);
+
+ public final IPlainSigner signer;
+
+ @SneakyThrows
+ private static Properties getBaseProperties() {
+ try (InputStream in = new FileInputStream(System.getProperty(ExternSignServlet.PDF_AS_WEB_CONF))) {
+ val props = new Properties();
+ props.load(in);
+ return props;
+ }
+ }
+
+ @SneakyThrows
+ private static void injectProperties(Map<String, String> overlay) {
+ val props = getBaseProperties();
+ if (overlay != null) overlay.forEach(props::setProperty);
+ try (val out = new ByteArrayOutputStream()) {
+ props.store(out, "test config");
+ try (val in = new ByteArrayInputStream(out.toByteArray())) {
+ WebConfiguration.configure(in);
+ PdfAsHelper.reloadConfig();
+ PdfAsHelper.init();
+ }
+ }
+ }
+
+ @SneakyThrows
+ public MockMoa() {
+ try {
+ KeyStore ks = KeyStore.getInstance("PKCS12");
+ try (InputStream is = MockMoaSigningTest.class.getResourceAsStream("/config/pdfas/test.p12")) {
+ ks.load(is, "123456".toCharArray());
+ }
+ val alias = ks.aliases().nextElement();
+ val privateKey = (PrivateKey) ks.getKey(alias, "123456".toCharArray());
+ val certificate = new X509Certificate(ks.getCertificate(alias).getEncoded());
+ signer = new PAdESSignerKeystore(privateKey, certificate);
+ } catch (Exception e) {
+ throw Lombok.sneakyThrow(e);
+ }
+
+ // inject ourselves into the configuration
+ injectProperties(Map.of(
+ "moal."+keyIdentifier+".enabled", "true",
+ "moal."+keyIdentifier+".url", endpointURL,
+ "moal."+keyIdentifier+".KeyIdentifier", "KG_TEST",
+ "moal."+keyIdentifier+".Certificate",
+ "base64:"+Base64.getEncoder().encodeToString(signer.getCertificate(null).getEncoded())
+ ));
+ }
+
+ @Override
+ public CreateCMSSignatureResponseType createCMSSignature(CreateCMSSignatureRequest body) throws MOAFault {
+ val signatureInfoList = body.getSingleSignatureInfo();
+ Assertions.assertEquals(1, signatureInfoList.size());
+ val signatureInfo = signatureInfoList.get(0);
+ val dataObjectInfo = signatureInfo.getDataObjectInfo();
+ Assertions.assertEquals("detached", dataObjectInfo.getStructure());
+ val dataObject = dataObjectInfo.getDataObject();
+ Assertions.assertEquals("application/pdf", dataObject.getMetaInfo().getMimeType());
+ val content = dataObject.getContent().getBase64Content();
+ Assertions.assertNotEquals(0, content.length);
+ Assertions.assertEquals("KG_TEST", body.getKeyIdentifier());
+ try {
+ val cms = signer.sign(content, null, null, null);
+ val response = new CreateCMSSignatureResponseType();
+ response.getCMSSignatureOrErrorResponse().add(cms);
+ return response;
+ } catch (PdfAsException e) {
+ throw new MOAFault("Failed to create detached CMS in fake MOA", e);
+ }
+ }
+
+ @Override
+ public CreateXMLSignatureResponseType createXMLSignature(CreateXMLSignatureRequest body) throws MOAFault {
+ throw new IllegalStateException("We do not create XML signatures in this house.");
+ }
+
+ public void close() {
+ endpoint.stop();
+ // remove the injected overlay
+ injectProperties(null);
+ }
+ }
+
+ @Test
+ @SneakyThrows
+ public void signWithMockMOA() {
+ try (val watcher = TestUtils.OperationCountWatcher(mvc, "operation:sign", "status:ok")) {
+ try (MockMoa moa = new MockMoa()) {
+
+ final String pdf = Base64.getEncoder().encodeToString(
+ IOUtils.toByteArray(JsonApiTest.class.getResourceAsStream("/data/enc_own.pdf")));
+
+ final String signRequestID = UUID.randomUUID().toString();
+ final String signRequest = om.writeValueAsString(
+ Map.of(
+ "requestID", signRequestID,
+ "inputData", pdf,
+ "parameters", Map.of(
+ "connector", "moa",
+ "keyIdentifier", moa.keyIdentifier,
+ "transactionId", UUID.randomUUID().toString()
+ )
+ )
+ );
+
+ final String signResponse = mvc.perform(
+ post("/api/v2/sign/single")
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON)
+ .content(signRequest)
+ )
+ .andExpect(status().isOk())
+ .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
+ .andExpect(jsonPath("$.requestID").value(signRequestID))
+ .andExpect(jsonPath("$.signedPDF").isNotEmpty())
+ .andExpect(jsonPath("$.verificationResponse").exists())
+ .andReturn().getResponse().getContentAsString();
+
+ final byte[] signedPDF = Base64.getDecoder().decode(JsonPath.<String>read(signResponse, "$.signedPDF"));
+ assertArrayEquals("Signed data looks PDF-ish (%PDF- header)",
+ new byte[]{'%', 'P', 'D', 'F', '-'}, Arrays.copyOfRange(signedPDF, 0, 5));
+ }
+ }
+ }
+
+ @Test
+ @SneakyThrows
+ public void moaTimeout() {
+ try (MockMoa moa = new MockMoa() {
+ @Override
+ @SneakyThrows
+ public CreateCMSSignatureResponseType createCMSSignature(CreateCMSSignatureRequest body) throws MOAFault {
+ // this will cause a timeout
+ Thread.sleep(300 * 1000);
+ throw new RuntimeException("unreachable");
+ }
+ }) {
+ final String pdf = Base64.getEncoder().encodeToString(
+ IOUtils.toByteArray(JsonApiTest.class.getResourceAsStream("/data/enc_own.pdf")));
+
+ final String signRequestID = UUID.randomUUID().toString();
+ final String signRequest = om.writeValueAsString(
+ Map.of(
+ "requestID", signRequestID,
+ "inputData", pdf,
+ "parameters", Map.of(
+ "connector", "moa",
+ "keyIdentifier", moa.keyIdentifier,
+ "transactionId", UUID.randomUUID().toString()
+ )
+ )
+ );
+
+ mvc.perform(
+ post("/api/v2/sign/single")
+ .contentType(MediaType.APPLICATION_JSON)
+ .accept(MediaType.APPLICATION_JSON)
+ .content(signRequest)
+ )
+ .andExpect(status().isOk())
+ .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
+ .andExpect(jsonPath("$.requestID").value(signRequestID))
+ .andExpect(jsonPath("$.signedPDF").isEmpty())
+ .andExpect(jsonPath("$.errorCode").value(11022));
+ }
+ }
+}
diff --git a/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/SimpleSignServletTest.java b/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/SimpleSignServletTest.java
index 7c020b17..8ab9cfaf 100644
--- a/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/SimpleSignServletTest.java
+++ b/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/SimpleSignServletTest.java
@@ -6,11 +6,11 @@ import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.ServletConfig;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.junit.BeforeClass;
import org.junit.Ignore;
diff --git a/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/SimpleVerifyServletTest.java b/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/SimpleVerifyServletTest.java
index 046a1203..e0075940 100644
--- a/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/SimpleVerifyServletTest.java
+++ b/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/SimpleVerifyServletTest.java
@@ -9,11 +9,11 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.ServletConfig;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
diff --git a/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/TestUtils.java b/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/TestUtils.java
new file mode 100644
index 00000000..4ee606bb
--- /dev/null
+++ b/pdf-as-web/src/test/java/at/gv/egiz/pdfas/web/test/TestUtils.java
@@ -0,0 +1,30 @@
+package at.gv.egiz.pdfas.web.test;
+
+import com.jayway.jsonpath.JsonPath;
+import lombok.val;
+import org.junit.Assert;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class TestUtils {
+ public static double getOperationCount(MockMvc mvc, String... tags) throws Exception {
+ val builder = MockMvcRequestBuilders.get("/actuator/metrics/pdfas_requests");
+ Arrays.stream(tags).forEach(tag -> builder.param("tag", tag));
+ val result =
+ mvc.perform(builder).andReturn().getResponse();
+ if (result.getStatus() == 404) return 0.0;
+ Assert.assertEquals(200, result.getStatus());
+ return JsonPath.<List<Double>>read(
+ result.getContentAsString(),
+ "$.measurements[?(@.statistic == 'COUNT')].value")
+ .get(0);
+ }
+
+ public static AutoCloseable OperationCountWatcher(MockMvc mvc, String... tags) throws Exception {
+ val initialCount = TestUtils.getOperationCount(mvc, tags);
+ return () -> Assert.assertEquals(initialCount+1.0, TestUtils.getOperationCount(mvc, tags), 0.0001);
+ }
+}