diff options
Diffstat (limited to 'pdf-as-web/src/main/java/at')
47 files changed, 1238 insertions, 949 deletions
diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/PdfAsWeb.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/PdfAsWeb.java new file mode 100644 index 00000000..2324d4ea --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/PdfAsWeb.java @@ -0,0 +1,32 @@ +package at.gv.egiz.pdfas.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@SpringBootApplication +public class PdfAsWeb extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder createSpringApplicationBuilder() { + SpringApplicationBuilder builder = new SpringApplicationBuilder(); + builder.sources(PdfAsWeb.class); + return builder; + + } + + public static void main(String[] args) { + log.info("=============== Initializing Spring-Boot context! ==============="); + final SpringApplication springApp = new SpringApplication(PdfAsWeb.class); + + log.debug("Run SpringBoot initialization process ... "); + springApp.run(args); + + log.info("Initialization of PDF-AS-Web finished."); + + } +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/PdfAsSpringBootApplicationContextInitializer.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/PdfAsSpringBootApplicationContextInitializer.java new file mode 100644 index 00000000..184030d2 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/PdfAsSpringBootApplicationContextInitializer.java @@ -0,0 +1,71 @@ +package at.gv.egiz.pdfas.web.config; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.PropertiesPropertySource; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class PdfAsSpringBootApplicationContextInitializer + implements ApplicationContextInitializer<ConfigurableApplicationContext> { + + private static final String SYSTEMD_PROP_NAME = "pdf-as-web.conf"; + private static final String FILE_PREFIX = "file:"; + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + final String rawConfigPath = System.getProperty(SYSTEMD_PROP_NAME); + + if (StringUtils.isBlank(rawConfigPath)) { + log.info("No SystemD property '{}' found. No external configuration loaded.", SYSTEMD_PROP_NAME); + return; + } + + log.debug("Found configuration source from SystemD property '{}'.", SYSTEMD_PROP_NAME); + + final String configPath = stripFilePrefix(rawConfigPath); + injectConfiguration(Path.of(configPath), applicationContext); + } + + private static String stripFilePrefix(String configPath) { + return configPath.startsWith(FILE_PREFIX) + ? configPath.substring(FILE_PREFIX.length()) + : configPath; + } + + private void injectConfiguration( + Path configPath, + ConfigurableApplicationContext applicationContext) { + + if (!Files.isRegularFile(configPath)) { + log.error("Configuration from SystemD property '{}' does not exist or is not a file: {}", + SYSTEMD_PROP_NAME, configPath); + return; + } + + try (InputStream inputStream = Files.newInputStream(configPath)) { + final Properties properties = new Properties(); + properties.load(inputStream); + + applicationContext + .getEnvironment() + .getPropertySources() + .addFirst(new PropertiesPropertySource(SYSTEMD_PROP_NAME, properties)); + + log.info("Loaded configuration source from SystemD property '{}': {}", + SYSTEMD_PROP_NAME, configPath); + + } catch (IOException e) { + log.error("Configuration from SystemD property '{}' at location '{}' cannot be loaded.", + SYSTEMD_PROP_NAME, configPath, e); + } + } +}
\ No newline at end of file diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/PdfAsWebSpringConfiguration.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/PdfAsWebSpringConfiguration.java new file mode 100644 index 00000000..2a64307a --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/PdfAsWebSpringConfiguration.java @@ -0,0 +1,18 @@ +package at.gv.egiz.pdfas.web.config; + +import lombok.Getter; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class PdfAsWebSpringConfiguration { + /** path to pdf-as-web.properties as loaded from spring sources */ + @Value("${pdf-as-web.conf}") + @Getter + @NonNull String pdfAsWebConfPath; +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java index 7177541c..f781b258 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java @@ -25,6 +25,7 @@ package at.gv.egiz.pdfas.web.config; import java.io.File; import java.io.FileInputStream; +import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -67,6 +68,7 @@ public class WebConfiguration implements IConfigurationConstants { public static final String MOA_LIST = "moal"; public static final String MOA_URL = "url"; + public static final String MOA_TIMEOUT = "timeout"; public static final String MOA_KEYID = "KeyIdentifier"; public static final String MOA_CERT = "Certificate"; @@ -115,7 +117,7 @@ public class WebConfiguration implements IConfigurationConstants { public static final String UPLOAD_FILESIZE_THRESHOLD = "web.upload.filesizeThreshold"; public static final String UPLOAD_MAX_FILESIZE = "web.upload.filesizeMax"; - public static final String UPLOAD_MAX_REQUESTSIZE = "web.upload.RequestsizeMax"; + public static final String UPLOAD_MAX_REQUESTSIZE = "web.upload.requestsizeMax"; public static final String PLACEHOLDER_GENERATOR_ENABLED = "qr.placeholder.generator.enabled"; @@ -123,32 +125,62 @@ public class WebConfiguration implements IConfigurationConstants { private static final int MAX_FILE_SIZE = 1024 * 1024 * 40; // 40MB private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 50; // 50MB - private static Properties properties = new Properties(); - private static Properties hibernateProps = new Properties(); + private static final Properties properties = new Properties(); + private static final Properties hibernateProps = new Properties(); private static final Logger logger = LoggerFactory .getLogger(WebConfiguration.class); - private static List<String> whiteListregEx = new ArrayList<String>(); - private static List<String> overwritewhiteListregEx = new ArrayList<String>(); + private static final List<String> whiteListregEx = new ArrayList<String>(); + private static final List<String> overwritewhiteListregEx = new ArrayList<String>(); + + public static void configure(String configFile) { + try (InputStream is = new FileInputStream(configFile)) { + logger.info("Loading PDF-AS Web configuration from '{}'...", configFile); + configure(is); + } catch (Exception e) { + logger.error("Failed to load configuration {}", configFile, e); + if (e instanceof RuntimeException ex) + throw ex; + else + throw new RuntimeException(e); + } + } - public static void configure(String config) { + public static void configure(InputStream config) { properties.clear(); + hibernateProps.clear(); whiteListregEx.clear(); overwritewhiteListregEx.clear(); try { - properties.load(new FileInputStream(config)); + properties.load(config); } catch (Exception e) { - logger.error("Failed to load configuration: " + e.getMessage()); + logger.error("Failed to load configuration.", e); throw new RuntimeException(e); } + { // validate + if (properties.isEmpty()) { + logger.error("No properties were loaded from web configuration. You likely did not specify `pdf-as-web.conf` correctly."); + throw new RuntimeException("No properties were loaded from the web configuration. Check if you specified `pdf-as-web.conf` correctly."); + } + String pdfASDir = getPdfASDir(); + if (pdfASDir == null) { + logger.error("Please configure the PDF-AS working directory in the web configuration"); + throw new RuntimeException( + "Please configure PDF-AS working directory in the web configuration"); + } + File f = new File(pdfASDir); + if (!f.exists() || !f.isDirectory()) { + logger.error("PDF-AS working directory does not exist or is not a directory!: {}", pdfASDir); + throw new RuntimeException("PDF-AS working directory does not exists or is not a directory!"); + } + } + if (isWhiteListEnabled()) { - Iterator<Object> keyIt = properties.keySet().iterator(); - while (keyIt.hasNext()) { - Object keyObj = keyIt.next(); + for (Object keyObj : properties.keySet()) { if (keyObj != null) { String key = keyObj.toString(); if (key.startsWith(WHITELIST_VALUE_PRE)) { @@ -163,9 +195,7 @@ public class WebConfiguration implements IConfigurationConstants { } if (isAllowExtOverwrite()) { - Iterator<Object> keyIt = properties.keySet().iterator(); - while (keyIt.hasNext()) { - Object keyObj = keyIt.next(); + for (Object keyObj : properties.keySet()) { if (keyObj != null) { String key = keyObj.toString(); if (key.startsWith(ALLOW_EXT_WHITELIST_VALUE_PRE)) { @@ -179,9 +209,7 @@ public class WebConfiguration implements IConfigurationConstants { } } - Iterator<Object> keyIt = properties.keySet().iterator(); - while (keyIt.hasNext()) { - Object keyObj = keyIt.next(); + for (Object keyObj : properties.keySet()) { if (keyObj != null) { String key = keyObj.toString(); if (key.startsWith(HIBERNATE_PREFIX)) { @@ -196,9 +224,7 @@ public class WebConfiguration implements IConfigurationConstants { if (hibernateProps.size() != 0) { logger.debug("DB Properties: "); - Iterator<Object> hibkeyIt = hibernateProps.keySet().iterator(); - while (hibkeyIt.hasNext()) { - Object keyObj = hibkeyIt.next(); + for (Object keyObj : hibernateProps.keySet()) { if (keyObj != null) { String key = keyObj.toString(); String value = hibernateProps.getProperty(key); @@ -206,22 +232,6 @@ public class WebConfiguration implements IConfigurationConstants { } } } - - String pdfASDir = getPdfASDir(); - if (pdfASDir == null) { - logger.error("Please configure pdf as working directory in the web configuration"); - throw new RuntimeException( - "Please configure pdf as working directory in the web configuration"); - } - - File f = new File(pdfASDir); - - if (!f.exists() || !f.isDirectory()) { - logger.error("Pdf As working directory does not exists or is not a directory!: " - + pdfASDir); - throw new RuntimeException( - "Pdf As working directory does not exists or is not a directory!"); - } } public static String getPublicURL() { @@ -314,12 +324,7 @@ public class WebConfiguration implements IConfigurationConstants { public static boolean isAllowExtOverwrite() { String value = properties.getProperty(ALLOW_EXT_OVERWRITE); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(value); } public static synchronized boolean isOverwriteAllowed(String key) { @@ -344,47 +349,31 @@ public class WebConfiguration implements IConfigurationConstants { public static boolean isJSONAPIEnabled() { String value = properties.getProperty(JSON_API_ENABLED); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(value); } public static boolean isKeepSignedDocument() { String value = properties.getProperty(KEEP_SIGNED_DOCUMENT); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(value); } public static boolean isMoaEnabled(String keyIdentifier) { String value = properties.getProperty(MOA_LIST + "." + keyIdentifier + ".enabled"); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(value); } public static boolean isQRPlaceholderGenerator() { String value = properties.getProperty(PLACEHOLDER_GENERATOR_ENABLED); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(value); } public static String getMoaURL(String keyIdentifier) { return properties.getProperty(MOA_LIST + "." + keyIdentifier + "." + MOA_URL); } + + public static String getMoaTimeout(String keyIdentifier) { + return properties.getProperty(MOA_LIST + "." + keyIdentifier + "." + MOA_TIMEOUT); + } public static String getMoaKeyID(String keyIdentifier) { return properties.getProperty(MOA_LIST + "." + keyIdentifier + "." + MOA_KEYID); @@ -429,121 +418,57 @@ public class WebConfiguration implements IConfigurationConstants { public static boolean getMOASSEnabled() { String value = properties.getProperty(MOA_SS_ENABLED); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(value); } public static boolean getKeystoreDefaultEnabled() { String value = properties.getProperty(KEYSTORE_DEFAULT_ENABLED); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(value); } public static boolean getKeystoreEnabled(String keyIdentifier) { String value = properties.getProperty(KEYSTORE_LIST + "." + keyIdentifier + "." + KEYSTORE_ENABLED); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(value); } public static boolean getLocalBKUEnabled() { - String value = properties.getProperty(LOCAL_BKU_ENABLED); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(properties.getProperty(LOCAL_BKU_ENABLED)); } public static boolean getMobileBKUEnabled() { - String value = properties.getProperty(MOBILE_BKU_ENABLED); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(properties.getProperty(MOBILE_BKU_ENABLED)); } public static boolean getOnlineBKUEnabled() { - String value = properties.getProperty(ONLINE_BKU_ENABLED); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(properties.getProperty(ONLINE_BKU_ENABLED)); } public static boolean getSL20Enabled() { - String value = properties.getProperty(SL20_BKU_ENABLED); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(properties.getProperty(SL20_BKU_ENABLED)); } public static boolean getSoapSignEnabled() { - String value = properties.getProperty(SOAP_SIGN_ENABLED); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(properties.getProperty(SOAP_SIGN_ENABLED)); } public static boolean isSoapSignWithVerifyEnabled() { String value = properties.getProperty(SOAP_SIGN_WITH_VERIFY_ENABLED); if (value != null) { - return value.equals("true"); - + return Boolean.parseBoolean(value); } return getSoapSignEnabled(); } public static boolean getSoapVerifyEnabled() { - String value = properties.getProperty(SOAP_VERIFY_ENABLED); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(properties.getProperty(SOAP_VERIFY_ENABLED)); } public static boolean isShowErrorDetails() { - String value = properties.getProperty(ERROR_DETAILS); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(properties.getProperty(ERROR_DETAILS)); } public static boolean isWhiteListEnabled() { - String value = properties.getProperty(WHITELIST_ENABLED); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(properties.getProperty(WHITELIST_ENABLED)); } public static synchronized boolean isProvidePdfURLinWhitelist(String url) { @@ -598,13 +523,7 @@ public class WebConfiguration implements IConfigurationConstants { } public static boolean getReloadEnabled() { - String value = properties.getProperty(RELOAD_ENABLED); - if (value != null) { - if (value.equals("true")) { - return true; - } - } - return false; + return Boolean.parseBoolean(properties.getProperty(RELOAD_ENABLED)); } public static int getFilesizeThreshold() { diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/exception/PdfAsSecurityLayerException.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/exception/PdfAsSecurityLayerException.java index 08577034..bc5a4d02 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/exception/PdfAsSecurityLayerException.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/exception/PdfAsSecurityLayerException.java @@ -33,5 +33,10 @@ public class PdfAsSecurityLayerException extends Exception { public PdfAsSecurityLayerException(String info, int errorcode) { super("SecurityLayer Error: [" + errorcode + "] " + info); } + + public PdfAsSecurityLayerException(String info, int errorcode, Exception e) { + super("SecurityLayer Error: [" + errorcode + "] " + info, e); + + } } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/ExceptionCatchFilter.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/ExceptionCatchFilter.java index 5d1abc15..65fcdd33 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/ExceptionCatchFilter.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/ExceptionCatchFilter.java @@ -28,22 +28,22 @@ import java.util.Collections; import java.util.Enumeration; import java.util.List; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - +import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletFileUpload; import org.apache.commons.lang3.StringUtils; import org.slf4j.MDC; import com.beust.jcommander.Strings; import com.beust.jcommander.internal.Lists; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; import lombok.extern.slf4j.Slf4j; /** @@ -90,22 +90,25 @@ public class ExceptionCatchFilter implements Filter { throws IOException, ServletException { try { - if (request instanceof HttpServletRequest) { - HttpServletRequest httpRequest = (HttpServletRequest) request; - - HttpSession session = httpRequest.getSession(isStatefull(httpRequest.getServletPath())); + if (request instanceof HttpServletRequest httpRequest) { + + HttpSession session = httpRequest.getSession(isStatefull(httpRequest.getServletPath())); String sessionId = session != null ? session.getId() : "-"; MDC.put("SESSION_ID", sessionId); - log.info("Access from IP: {}", getClientIpAddr(httpRequest)); - log.info("Access to: {} in Session: {}", httpRequest.getServletPath(), sessionId); - - log.debug("Processing Parameters into Attributes"); - @SuppressWarnings("unchecked") - Enumeration<String> parameterNames = httpRequest.getParameterNames(); - while (parameterNames.hasMoreElements()) { - String name = parameterNames.nextElement(); - String value = httpRequest.getParameter(name); - request.setAttribute(name, value); + log.debug("Access from IP: {}", getClientIpAddr(httpRequest)); + log.debug("Access to: {} in Session: {}", httpRequest.getServletPath(), sessionId); + + if (!JakartaServletFileUpload.isMultipartContent(httpRequest)) { + log.debug("Processing Parameters into Attributes"); + @SuppressWarnings("unchecked") + Enumeration<String> parameterNames = httpRequest.getParameterNames(); + while (parameterNames.hasMoreElements()) { + String name = parameterNames.nextElement(); + String value = httpRequest.getParameter(name); + request.setAttribute(name, value); + } + } else { + log.debug("Skipping global parameter parsing for multipart request"); } } @@ -113,13 +116,11 @@ public class ExceptionCatchFilter implements Filter { chain.doFilter(request, response); } finally { - if (response instanceof HttpServletResponse) { - HttpServletResponse resp = (HttpServletResponse) response; - log.debug("Got response status: {}", resp.getStatus()); - - } else { - log.warn("Response is not a HttpServletResponse!"); - + if (response instanceof HttpServletResponse) { + HttpServletResponse resp = (HttpServletResponse) response; + log.debug("Got response status: {}", resp.getStatus()); + } else { + log.warn("Response is not a HttpServletResponse!"); } } } catch (Throwable e) { @@ -139,7 +140,10 @@ public class ExceptionCatchFilter implements Filter { } private boolean isStatefull(String contextPath) { - boolean statefull = !statelessPaths.contains(contextPath); + boolean statefull = !statelessPaths.stream() + .filter(el -> contextPath.startsWith(el)) + .findFirst() + .isPresent(); log.trace("ServletPath: {} is marked as {}", contextPath, statefull ? "statefull" : "stateless"); return statefull; diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/UserAgentFilter.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/UserAgentFilter.java index ef7d391d..15cadb48 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/UserAgentFilter.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/filter/UserAgentFilter.java @@ -2,13 +2,13 @@ package at.gv.egiz.pdfas.web.filter; import java.io.IOException; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java index 9900dda4..554d79d1 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java @@ -32,28 +32,27 @@ import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.security.cert.CertificateException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; import javax.imageio.ImageIO; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import javax.xml.bind.JAXBElement; -import javax.xml.ws.WebServiceException; - +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import jakarta.xml.bind.JAXBElement; +import jakarta.xml.ws.WebServiceException; + +import lombok.Getter; +import lombok.val; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringEscapeUtils; import org.apache.http.entity.ContentType; import com.google.gson.JsonArray; @@ -155,7 +154,8 @@ public class PdfAsHelper { private static PdfAs pdfAs; private static ObjectFactory of = new ObjectFactory(); - private static Configuration pdfAsConfig; + @Getter + private static Configuration pdfAsConfig; static { reloadConfig(); @@ -169,44 +169,17 @@ public class PdfAsHelper { public static synchronized void reloadConfig() { log.info("Creating PDF-AS"); - pdfAs = PdfAsFactory.createPdfAs(new File(WebConfiguration - .getPdfASDir())); + val workdir = WebConfiguration.getPdfASDir(); + if (workdir == null) { + throw new IllegalStateException( + "WebConfiguration is not yet initialized. This is an application start-up order bug."); + } + pdfAs = PdfAsFactory.createPdfAs(new File(workdir)); pdfAsConfig = pdfAs.getConfiguration(); log.info("Creating PDF-AS done"); } - public static Configuration getPdfAsConfig() { - return pdfAsConfig; - } - - private static void validatePdfSize(HttpServletRequest request, - HttpServletResponse response, byte[] pdfData) - throws PdfAsWebException { - // Try to check num-bytes - String pdfSizeString = PdfAsParameterExtractor.getNumBytes(request); - if (pdfSizeString != null) { - long pdfSize = -1; - try { - pdfSize = Long.parseLong(pdfSizeString); - } catch (NumberFormatException e) { - throw new PdfAsWebException( - PdfAsParameterExtractor.PARAM_NUM_BYTES - + " parameter has to be a positiv number!", e); - } - if (pdfSize <= 0) { - throw new PdfAsWebException( - "Invalid PDF Size! Has to bigger than zero!"); - } - - if (pdfData.length != pdfSize) { - throw new PdfAsWebException("Signature Data Size and " - + PdfAsParameterExtractor.PARAM_NUM_BYTES - + " missmatch!"); - } - } - } - - public static String buildPosString(HttpServletRequest request, + public static String buildPosString(HttpServletRequest request, HttpServletResponse response) throws PdfAsWebException { String posP = PdfAsParameterExtractor.getSigPosP(request); String posX = PdfAsParameterExtractor.getSigPosX(request); @@ -268,22 +241,22 @@ public class PdfAsHelper { sb.append("w:auto;"); } } - sb.append("w:" + posW.trim() + ";"); + sb.append("w:").append(posW.trim()).append(";"); } else { sb.append("w:auto;"); } if (posP != null) { - if (!(posP.equals("auto") || posP.equals("new"))) { + if (!(posP.equals("auto") || posP.equals("new") || posP.equals("last"))) { try { Integer.parseInt(posP); } catch (NumberFormatException e) { throw new PdfAsWebException( PdfAsParameterExtractor.PARAM_SIG_POS_P - + " has invalid value! (auto | new )"); + + " has invalid value! (auto | new | last)"); } } - sb.append("p:" + posP.trim() + ";"); + sb.append("p:").append(posP.trim()).append(";"); } else { sb.append("p:auto;"); } @@ -298,7 +271,7 @@ public class PdfAsHelper { + " has invalid value!", e); } } - sb.append("r:" + posR.trim() + ";"); + sb.append("r:").append(posR.trim()).append(";"); } else { sb.append("r:0;"); } @@ -315,7 +288,7 @@ public class PdfAsHelper { sb.append("f:0;"); } } - sb.append("f:" + posF.trim() + ";"); + sb.append("f:").append(posF.trim()).append(";"); } else { sb.append("f:0;"); } @@ -349,9 +322,7 @@ public class PdfAsHelper { verifyParameter.setConfiguration(config); verifyParameter.setWhichSignature(signIdx); - List<VerifyResult> results = pdfAs.verify(verifyParameter); - - return results; + return pdfAs.verify(verifyParameter); } public static List<VerifyResult> synchronousVerify(byte[] pdfData, @@ -372,9 +343,7 @@ public class PdfAsHelper { verifyParameter.setConfiguration(config); verifyParameter.setWhichSignature(signIdx); - List<VerifyResult> results = pdfAs.verify(verifyParameter); - - return results; + return pdfAs.verify(verifyParameter); } public static PdfasSignResponse synchronousServerSignature(PdfasSignRequest internalReq) throws Exception { @@ -384,10 +353,9 @@ public class PdfAsHelper { respBuilder.transactionId(internalReq.getCoreParams().getTransactionId()); // sign each document - Iterator<DocumentToSign> docsToSign = internalReq.getInput().iterator(); - while(docsToSign.hasNext()) { - respBuilder.signedPdf(synchronousServerSignature(docsToSign.next(), internalReq.getCoreParams())); - + for (DocumentToSign documentToSign : internalReq.getInput()) { + respBuilder.signedPdf(synchronousServerSignature(documentToSign, internalReq.getCoreParams())); + } log.debug("Signing process finished."); @@ -419,39 +387,31 @@ public class PdfAsHelper { new ByteArrayDataSource(documentToSign.getInputData()), baos); // Get Connector + val connector = coreParams.getConnector(); + val keyIdentifier = coreParams.getKeyIdentifier(); + PdfAsHelper.checkConnectorSupported(connector, keyIdentifier); IPlainSigner signer; - if (coreParams.getConnector().equals(Connector.MOA)) { - String keyIdentifier = coreParams.getKeyIdentifier(); + if (connector == Connector.MOA) { if (keyIdentifier != null) { - if (!WebConfiguration.isMoaEnabled(keyIdentifier)) { - throw new PdfAsWebException("MOA connector [" - + keyIdentifier + "] disabled or not existing."); - } - String url = WebConfiguration.getMoaURL(keyIdentifier); + String timeout = WebConfiguration.getMoaTimeout(keyIdentifier); String keyId = WebConfiguration.getMoaKeyID(keyIdentifier); String certificate = WebConfiguration .getMoaCertificate(keyIdentifier); config.setValue(IConfigurationConstants.MOA_SIGN_URL, url); + config.setValue(IConfigurationConstants.MOA_SIGN_TIMEOUT, timeout); config.setValue(IConfigurationConstants.MOA_SIGN_KEY_ID, keyId); config.setValue(IConfigurationConstants.MOA_SIGN_CERTIFICATE, certificate); - } else { - if (!WebConfiguration.getMOASSEnabled()) { - throw new PdfAsWebException("MOA connector disabled."); - } } signer = new PAdESSigner(new MOAConnector(config)); - } else if (coreParams.getConnector().equals(Connector.JKS)) { - String keyIdentifier = coreParams.getKeyIdentifier(); - - boolean ksEnabled = false; + } else if (connector == Connector.JKS) { String ksFile = null; String ksAlias = null; String ksPass = null; @@ -459,14 +419,12 @@ public class PdfAsHelper { String ksType = null; if (keyIdentifier != null) { - ksEnabled = WebConfiguration.getKeystoreEnabled(keyIdentifier); ksFile = WebConfiguration.getKeystoreFile(keyIdentifier); ksAlias = WebConfiguration.getKeystoreAlias(keyIdentifier); ksPass = WebConfiguration.getKeystorePass(keyIdentifier); ksKeyPass = WebConfiguration.getKeystoreKeyPass(keyIdentifier); ksType = WebConfiguration.getKeystoreType(keyIdentifier); } else { - ksEnabled = WebConfiguration.getKeystoreDefaultEnabled(); ksFile = WebConfiguration.getKeystoreDefaultFile(); ksAlias = WebConfiguration.getKeystoreDefaultAlias(); ksPass = WebConfiguration.getKeystoreDefaultPass(); @@ -474,27 +432,6 @@ public class PdfAsHelper { ksType = WebConfiguration.getKeystoreDefaultType(); } - if (!ksEnabled) { - if (keyIdentifier != null) { - throw new PdfAsWebException("JKS connector [" - + keyIdentifier + "] disabled or not existing."); - } else { - throw new PdfAsWebException( - "DEFAULT JKS connector disabled."); - } - } - - if (ksFile == null || ksAlias == null || ksPass == null - || ksKeyPass == null || ksType == null) { - if (keyIdentifier != null) { - throw new PdfAsWebException("JKS connector [" - + keyIdentifier + "] not correctly configured."); - } else { - throw new PdfAsWebException( - "DEFAULT JKS connector not correctly configured."); - } - } - signer = new PAdESSignerKeystore(ksFile, ksAlias, ksPass, ksKeyPass, ksType); @@ -552,28 +489,26 @@ public class PdfAsHelper { SignResult signResult = pdfAs.sign(signParameter); - PDFASVerificationResponse verResponse = new PDFASVerificationResponse(); - verResponse.setSignerCertificate(signResult.getSignerCertificate().getEncoded()); + PDFASVerificationResponse verResponse = new PDFASVerificationResponse(); + verResponse.setSignerCertificate(signResult.getSignerCertificate().getEncoded()); - - SignedDocument signPdfDoc = SignedDocument.builder() - .signingTimestamp(Long.valueOf(System.currentTimeMillis())) - .outputData(baos.toByteArray()) - .fileName(documentToSign.getFileName()) - .verificationResponse(verResponse) - .signerCertificate(Base64.encodeBase64String(signResult.getSignerCertificate().getEncoded())) - .build(); - - return signPdfDoc; + + return SignedDocument.builder() + .signingTimestamp(System.currentTimeMillis()) + .outputData(baos.toByteArray()) + .fileName(documentToSign.getFileName()) + .verificationResponse(verResponse) + .signerCertificate(Base64.encodeBase64String(signResult.getSignerCertificate().getEncoded())) + .build(); } public static void startSignatureJson(HttpServletRequest request, HttpServletResponse response, - ServletContext context, String connector, PdfasSignRequest pdfAsRequest) throws Exception { + ServletContext context, Connector connector, PdfasSignRequest pdfAsRequest) throws Exception { HttpSession session = request.getSession(); log.info("Starting signature in session: " + session.getId()); - session.setAttribute(PDF_PROCESSING_REQUEST, pdfAsRequest); + session.setAttribute(PDF_PROCESSING_REQUEST, pdfAsRequest); StatusRequest statusRequest = initializeSigningContextForNewDocument(request, connector, pdfAsRequest); session.setAttribute(PDF_STATUS, statusRequest); @@ -581,7 +516,7 @@ public class PdfAsHelper { } public static void startSignature(HttpServletRequest request, HttpServletResponse response, - ServletContext context, String connector, PdfasSignRequest pdfAsRequest) throws Exception { + ServletContext context, Connector connector, PdfasSignRequest pdfAsRequest) throws Exception { HttpSession session = request.getSession(); log.info("Starting signature in session: " + session.getId()); session.setAttribute(PDF_PROCESSING_REQUEST, pdfAsRequest); @@ -594,7 +529,7 @@ public class PdfAsHelper { } - private static StatusRequest initializeSigningContextForNewDocument(HttpServletRequest request, String connector, PdfasSignRequest pdfAsRequest) + private static StatusRequest.Stage1 initializeSigningContextForNewDocument(HttpServletRequest request, Connector connector, PdfasSignRequest pdfAsRequest) throws PdfAsWebException, WriterException, IOException, PdfAsException, PDFASError { HttpSession session = request.getSession(); @@ -606,7 +541,7 @@ public class PdfAsHelper { session.setAttribute(PDF_SL_INTERACTIVE, connector); // prepare first document - IPlainSigner signer = getSignerFromConnector(connector, config, session); + IPlainSigner signer = getSignerFromConnector(connector, session); session.setAttribute(PDF_SIGNER, signer); String qrCodeContent = PdfAsHelper.getQRCodeContent(request); @@ -619,7 +554,7 @@ public class PdfAsHelper { } - private static StatusRequest buildPdfasStatusRequestToSignSingleDocument(DocumentToSign pdfToSign, HttpSession session, IPlainSigner signer, + private static StatusRequest.Stage1 buildPdfasStatusRequestToSignSingleDocument(DocumentToSign pdfToSign, HttpSession session, IPlainSigner signer, CoreSignParams coreSignParams, String qrCodeContent, Configuration config) throws WriterException, IOException, PdfAsException, PDFASError { ByteArrayOutputStream baos = new ByteArrayOutputStream(); session.setAttribute(PDF_OUTPUT, baos); @@ -671,23 +606,14 @@ public class PdfAsHelper { } - private static IPlainSigner getSignerFromConnector(String connector, Configuration config, HttpSession session) throws PdfAsWebException { - if (connector.equals("bku") || connector.equals("onlinebku") - || connector.equals("mobilebku")) { - BKUSLConnector conn = new BKUSLConnector(config); - session.setAttribute(PDF_SL_CONNECTOR, conn); - return new PAdESSigner(conn); - - - } else if (connector.equals("sl20")) { - SL20Connector conn = new SL20Connector(config); - session.setAttribute(PDF_SL_CONNECTOR, conn); - return new PAdESSigner(conn); - - } else { - throw new PdfAsWebException( - "Invalid connector (bku | onlinebku | mobilebku | moa | jks | sl20)"); - } + private static IPlainSigner getSignerFromConnector(Connector connector, HttpSession session) throws PdfAsWebException { + val slConnector = switch(connector) { + case BKU, ONLINEBKU, MOBILEBKU -> new BKUSLConnector(generateBKUURL(connector)); + case SECLAYER20 -> new SL20Connector(WebConfiguration.getSecurityLayer20URL()); + default -> throw new PdfAsWebException("Invalid connector (bku | onlinebku | mobilebku | sl20)"); + }; + session.setAttribute(PDF_SL_CONNECTOR, slConnector); + return new PAdESSigner(slConnector); } public static byte[] getCertificate( @@ -738,7 +664,7 @@ public class PdfAsHelper { .getAttribute(PDF_STATUS); if(statusObject != null && statusObject instanceof StatusRequest) { StatusRequest statusRequest = (StatusRequest)statusObject; - if(statusRequest.needCertificate() || statusRequest.needSignature()) { + if (statusRequest instanceof StatusRequest.Stage1 || statusRequest instanceof StatusRequest.Stage2) { return true; } } @@ -756,21 +682,19 @@ public class PdfAsHelper { StatusRequest statusRequest = (StatusRequest) session .getAttribute(PDF_STATUS); - if (statusRequest == null) { + if (!(statusRequest instanceof StatusRequest.Stage1 statusRequest1)) { throw new PdfAsWebException("No Signature running in session:" + session.getId()); } - - statusRequest.setCertificate(certificate); - statusRequest = pdfAs.process(statusRequest); - session.setAttribute(PDF_STATUS, statusRequest); + val statusRequest2 = statusRequest1.setCertificate(certificate); + session.setAttribute(PDF_STATUS, statusRequest2); PdfAsHelper.process(request, response, context); } public static void injectSignature(HttpServletRequest request, HttpServletResponse response, - byte[] cmsSginature, + byte[] cmsSignature, ServletContext context) throws Exception { log.debug("Got CMS Signature Response"); @@ -779,14 +703,13 @@ public class PdfAsHelper { StatusRequest statusRequest = (StatusRequest) session .getAttribute(PDF_STATUS); - if (statusRequest == null) { + if (!(statusRequest instanceof StatusRequest.Stage2 statusRequest2)) { throw new PdfAsWebException("No Signature running in session:" + session.getId()); } - statusRequest.setSigature(cmsSginature); - statusRequest = pdfAs.process(statusRequest); - session.setAttribute(PDF_STATUS, statusRequest); + val statusRequest3 = statusRequest2.setSignature(cmsSignature); + session.setAttribute(PDF_STATUS, statusRequest3); PdfAsHelper.process(request, response, context); } @@ -800,14 +723,14 @@ public class PdfAsHelper { // IPlainSigner plainSigner = (IPlainSigner) session // .getAttribute(PDF_SIGNER); - String connector = (String) session.getAttribute(PDF_SL_INTERACTIVE); + Connector connector = (Connector) session.getAttribute(PDF_SL_INTERACTIVE); + PdfAsHelper.checkConnectorSupported(connector, null); - if (connector.equals("bku") || connector.equals("onlinebku") - || connector.equals("mobilebku")) { + if (connector == Connector.BKU || connector == Connector.ONLINEBKU || connector == Connector.MOBILEBKU) { BKUSLConnector bkuSLConnector = (BKUSLConnector) session .getAttribute(PDF_SL_CONNECTOR); - if (statusRequest.needCertificate()) { + if (statusRequest instanceof StatusRequest.Stage1) { log.debug("Needing Certificate from BKU"); // build SL Request to read certificate InfoboxReadRequestType readCertificateRequest = bkuSLConnector @@ -833,14 +756,15 @@ public class PdfAsHelper { throws Exception { HttpSession session = request.getSession(); - StatusRequest statusRequest = (StatusRequest) session.getAttribute(PDF_STATUS); + StatusRequest statusRequestGeneric = (StatusRequest) session.getAttribute(PDF_STATUS); PdfasSignRequest pdfAsRequest = (PdfasSignRequest) session.getAttribute(PDF_PROCESSING_REQUEST); // IPlainSigner plainSigner = (IPlainSigner) session // .getAttribute(PDF_SIGNER); - String connector = (String) session.getAttribute(PDF_SL_INTERACTIVE); + Connector connector = (Connector) session.getAttribute(PDF_SL_INTERACTIVE); + PdfAsHelper.checkConnectorSupported(connector, null); //load connector ISLConnector slConnector = (ISLConnector) session.getAttribute(PDF_SL_CONNECTOR); @@ -849,7 +773,7 @@ public class PdfAsHelper { if (!joseTools.isInitialized()) joseTools = null; - if (statusRequest.needCertificate()) { + if (statusRequestGeneric instanceof StatusRequest.Stage1 statusRequest) { log.debug("Needing Certificate from BKU"); // build SL Request to read certificate InfoboxReadRequestType readCertificateRequest = slConnector @@ -888,11 +812,10 @@ public class PdfAsHelper { response.setContentType("text/html"); response.getWriter().close(); - } else if (slConnector instanceof SL20Connector) { - //generate request for getCertificate command - SL20Connector sl20Connector = (SL20Connector)slConnector; - - //use 'SecureSigningKeypair' per default + } else if (slConnector instanceof SL20Connector sl20Connector) { + //generate request for getCertificate command + + //use 'SecureSigningKeypair' per default String keyId = SL20Connector.SecureSignatureKeypair; java.security.cert.X509Certificate x5cEnc = null; @@ -976,7 +899,7 @@ public class PdfAsHelper { } else throw new PdfAsWebException("Invalid connector: " + slConnector.getClass().getName()); - } else if (statusRequest.needSignature()) { + } else if (statusRequestGeneric instanceof StatusRequest.Stage2 statusRequest) { log.debug("Needing Signature from BKU"); // build SL Request for cms signature RequestPackage pack = slConnector.createCMSRequest( @@ -1077,7 +1000,7 @@ public class PdfAsHelper { log.trace("Write 'createCAdES' command to VDA: " + sl20CreateCAdES.toString()); StringWriter writer = new StringWriter(); writer.write(sl20CreateCAdES.toString()); - final byte[] content = writer.toString().getBytes("UTF-8"); + final byte[] content = writer.toString().getBytes(StandardCharsets.UTF_8); response.setStatus(HttpServletResponse.SC_OK); response.setContentLength(content.length); response.setContentType(ContentType.APPLICATION_JSON.toString()); @@ -1088,9 +1011,9 @@ public class PdfAsHelper { } - } else if (statusRequest.isReady()) { + } else if (statusRequestGeneric instanceof StatusRequest.Stage3 statusRequest) { log.debug("Single document is ready. Perform post-processing ... "); - SignResult result = pdfAs.finishSign(statusRequest); + SignResult result = statusRequest.finishSign(); ByteArrayOutputStream baos = (ByteArrayOutputStream) session.getAttribute(PDF_OUTPUT); baos.close(); @@ -1112,7 +1035,7 @@ public class PdfAsHelper { .getCode()); SignedDocument signPdfDoc = SignedDocument.builder() - .signingTimestamp(Long.valueOf(System.currentTimeMillis())) + .signingTimestamp(System.currentTimeMillis()) .outputData(baos.toByteArray()) .fileName(PdfAsHelper.getPDFFileName(request)) .verificationResponse(verResponse) @@ -1125,28 +1048,28 @@ public class PdfAsHelper { // check if more files are available if (pdfAsRequest.hasNext()) { log.debug("Find additional file, restarting signing process again ... "); - StatusRequestImpl nextStatusRequest = (StatusRequestImpl)initializeSigningContextForNewDocument(request, connector, pdfAsRequest); - nextStatusRequest.setCertificate(((StatusRequestImpl)statusRequest).getCertificate().getEncoded()); - nextStatusRequest.setNeedCertificate(true); - - statusRequest = pdfAs.process(nextStatusRequest); - session.setAttribute(PDF_STATUS, nextStatusRequest); - - PdfAsHelper.process(request, response, context); - session.setAttribute(PDF_STATUS, nextStatusRequest); + StatusRequest.Stage1 nextStatusRequest1 = initializeSigningContextForNewDocument(request, connector, pdfAsRequest); + StatusRequest.Stage2 nextStatusRequest2 = nextStatusRequest1.setCertificate( + statusRequest.getRequestedSignature().getCertificate().getEncoded()); + + session.setAttribute(PDF_STATUS, nextStatusRequest2); + + // recurse + PdfAsHelper.process(request, response, context); } else { - if (slConnector instanceof BKUSLConnector) { - PdfAsHelper.gotoProvidePdf(context, request, response); - - } else if (slConnector instanceof SL20Connector) { - //TODO: add code to send SL20 redirect command to redirect the user from DataURL connection to App Front-End connection - String callUrl = generateProvideURL(request, response); - String transactionId = (String) request.getAttribute(PdfAsHelper.PDF_SESSION_PREFIX + SL20Constants.SL20_TRANSACTIONID); - buildSL20RedirectResponse(request, response, transactionId, callUrl); - - } else - throw new PdfAsWebException("Invalid connector: " + slConnector.getClass().getName()); + if (slConnector instanceof BKUSLConnector) { + PdfAsHelper.gotoProvidePdf(context, request, response); + + } else if (slConnector instanceof SL20Connector) { + //TODO: add code to send SL20 redirect command to redirect the user from DataURL connection to App Front-End connection + String callUrl = generateProvideURL(request, response); + String transactionId = (String) request.getAttribute(PdfAsHelper.PDF_SESSION_PREFIX + SL20Constants.SL20_TRANSACTIONID); + buildSL20RedirectResponse(request, response, transactionId, callUrl); + + } else { + throw new PdfAsWebException("Invalid connector: " + slConnector.getClass().getName()); + } } @@ -1154,52 +1077,54 @@ public class PdfAsHelper { throw new PdfAsWebException("Invalid state!"); } - } + } private static String getTemplateSL() throws IOException { String xml = FileUtils.readFileToString( - FileUtils.toFile(PdfAsHelper.class.getResource("/template_sl.html"))); + FileUtils.toFile(PdfAsHelper.class.getResource("/template_sl.html")), + StandardCharsets.UTF_8); return xml; } public static String getErrorRedirectTemplateSL() throws IOException { - String xml = FileUtils.readFileToString(FileUtils - .toFile(PdfAsHelper.class - .getResource("/template_error_redirect.html"))); + String xml = FileUtils.readFileToString( + FileUtils.toFile(PdfAsHelper.class.getResource("/template_error_redirect.html")), + StandardCharsets.UTF_8); return xml; } public static String getProvideTemplate() throws IOException { - String xml = FileUtils - .readFileToString(FileUtils.toFile(PdfAsHelper.class - .getResource("/template_provide.html"))); + String xml = FileUtils.readFileToString( + FileUtils.toFile(PdfAsHelper.class.getResource("/template_provide.html")), + StandardCharsets.UTF_8); return xml; } public static String getErrorTemplate() throws IOException { - String xml = FileUtils.readFileToString(FileUtils - .toFile(PdfAsHelper.class.getResource("/template_error.html"))); + String xml = FileUtils.readFileToString( + FileUtils.toFile(PdfAsHelper.class.getResource("/template_error.html")), + StandardCharsets.UTF_8); return xml; } public static String getGenericTemplate() throws IOException { - String xml = FileUtils.readFileToString(FileUtils - .toFile(PdfAsHelper.class - .getResource("/template_generic_param.html"))); + String xml = FileUtils.readFileToString( + FileUtils.toFile(PdfAsHelper.class.getResource("/template_generic_param.html")), + StandardCharsets.UTF_8); return xml; } public static String getInvokeRedirectTemplateSL() throws IOException { - String xml = FileUtils.readFileToString(FileUtils - .toFile(PdfAsHelper.class - .getResource("/template_invoke_redirect.html"))); + String xml = FileUtils.readFileToString( + FileUtils.toFile(PdfAsHelper.class.getResource("/template_invoke_redirect.html")), + StandardCharsets.UTF_8); return xml; } public static String getInvokeRedirectTemplateMoreFiles() throws IOException { - String xml = FileUtils.readFileToString(FileUtils - .toFile(PdfAsHelper.class - .getResource("/template_invoke_redirect_more_files.html"))); + String xml = FileUtils.readFileToString( + FileUtils.toFile(PdfAsHelper.class.getResource("/template_invoke_redirect_more_files.html")), + StandardCharsets.UTF_8); return xml; } @@ -1442,28 +1367,19 @@ public class PdfAsHelper { } String baseURL = publicURL + PDF_USERENTRY_PAGE; - try { - return baseURL + "?" + UIEntryPointServlet.REQUEST_ID_PARAM + "=" - + URLEncoder.encode(storeId, "UTF-8"); - } catch (UnsupportedEncodingException e) { - log.warn("Encoding not supported for URL encoding", e); - } - return baseURL + "?" + UIEntryPointServlet.REQUEST_ID_PARAM + "=" - + storeId; - } - - public static String generateBKUURL(String connector) { - if (connector.equals("bku")) { - return WebConfiguration.getLocalBKUURL(); - } else if (connector.equals("onlinebku")) { - return WebConfiguration.getOnlineBKUURL(); - } else if (connector.equals("mobilebku")) { - return WebConfiguration.getHandyBKUURL(); - } else if (connector.equals("sl20")) { - return WebConfiguration.getSecurityLayer20URL(); - } - return WebConfiguration.getLocalBKUURL(); - } + return baseURL + "?" + UIEntryPointServlet.REQUEST_ID_PARAM + "=" + + URLEncoder.encode(storeId, StandardCharsets.UTF_8); + } + + public static String generateBKUURL(Connector connector) { + return switch (connector) { + case BKU -> WebConfiguration.getLocalBKUURL(); + case ONLINEBKU -> WebConfiguration.getOnlineBKUURL(); + case MOBILEBKU -> WebConfiguration.getHandyBKUURL(); + case SECLAYER20 -> WebConfiguration.getSecurityLayer20URL(); + default -> throw new IllegalStateException("Attempt to get BKU URL for connector: "+connector.name()); + }; + } public static void setFromDataUrl(HttpServletRequest request) { request.setAttribute(REQUEST_FROM_DU, (Boolean) true); @@ -1473,7 +1389,7 @@ public class PdfAsHelper { Object obj = request.getAttribute(REQUEST_FROM_DU); if (obj != null) { if (obj instanceof Boolean) { - return ((Boolean) obj).booleanValue(); + return (Boolean) obj; } } return false; @@ -1602,7 +1518,7 @@ public class PdfAsHelper { public static void setSignatureActive(HttpServletRequest request, boolean value) { HttpSession session = request.getSession(); - session.setAttribute(SIGNATURE_ACTIVE, new Boolean(value)); + session.setAttribute(SIGNATURE_ACTIVE, Boolean.valueOf(value)); } public static boolean isSignatureActive(HttpServletRequest request) { @@ -1672,7 +1588,7 @@ public class PdfAsHelper { log.trace("SL20 response to VDA: " + respContainer); StringWriter writer = new StringWriter(); writer.write(respContainer.toString()); - final byte[] content = writer.toString().getBytes("UTF-8"); + final byte[] content = writer.toString().getBytes(StandardCharsets.UTF_8); response.setStatus(HttpServletResponse.SC_OK); response.setContentLength(content.length); response.setContentType(ContentType.APPLICATION_JSON.toString()); @@ -1686,4 +1602,82 @@ public class PdfAsHelper { } } + private static boolean anyNull(Object... any) { + return (Arrays.stream(any).anyMatch(Objects::isNull)); + } + + public static void checkConnectorSupported(Connector connector, String keyIdentifier) throws PdfAsWebException { + if (connector == null) { + throw new PdfAsWebException("Invalid or unknown connector value"); + } else if (connector == Connector.BKU) { + if (!WebConfiguration.getLocalBKUEnabled()) { + throw new PdfAsWebException("Connector bku is not enabled in configuration"); + } + if (WebConfiguration.getLocalBKUURL() == null) { + throw new PdfAsWebException("Connector bku is not properly configured"); + } + } else if (connector == Connector.ONLINEBKU) { + if (!WebConfiguration.getOnlineBKUEnabled()) { + throw new PdfAsWebException("Connector onlinebku is not enabled in configuration"); + } + if (WebConfiguration.getOnlineBKUURL() == null) { + throw new PdfAsWebException("Connector onlinebku is not properly configured"); + } + } else if (connector == Connector.MOBILEBKU) { + if (!WebConfiguration.getMobileBKUEnabled()) { + throw new PdfAsWebException("Connector mobilebku is not enabled in configuration"); + } + if (WebConfiguration.getHandyBKUURL() == null) { + throw new PdfAsWebException("Connector mobilebku is not properly configured"); + } + } else if (connector == Connector.SECLAYER20) { + if (!WebConfiguration.getSL20Enabled()) { + throw new PdfAsWebException("Connector sl20 is not enabled in configuration"); + } + if (WebConfiguration.getSecurityLayer20URL() == null) { + throw new PdfAsWebException("Connector sl20 is not properly configured"); + } + } else if (connector == Connector.MOA) { + if (keyIdentifier != null) { + if (!WebConfiguration.isMoaEnabled(keyIdentifier)) { + throw new PdfAsWebException("Connector moa[keyId="+keyIdentifier+"] is not enabled in configuration"); + } + } else { + if (!WebConfiguration.getMOASSEnabled()) { + throw new PdfAsWebException("Connector moa[default] is not enabled in configuration"); + } + } + } else if (connector == Connector.JKS) { + if (keyIdentifier != null) { + if (!WebConfiguration.getKeystoreEnabled(keyIdentifier)) { + throw new PdfAsWebException("Connector jks[keyId="+keyIdentifier+"] is not enabled in configuration"); + } + if ( + anyNull( + WebConfiguration.getKeystoreFile(keyIdentifier), + WebConfiguration.getKeystoreAlias(keyIdentifier), + WebConfiguration.getKeystorePass(keyIdentifier), + WebConfiguration.getKeystoreKeyPass(keyIdentifier), + WebConfiguration.getKeystoreType(keyIdentifier))) + { + throw new PdfAsWebException("Connector jks[keyId="+keyIdentifier+"] is not properly configured"); + } + } else { + if (!WebConfiguration.getKeystoreDefaultEnabled()) { + throw new PdfAsWebException("Connector jks[default] is not enabled in configuration"); + } + if ( + anyNull( + WebConfiguration.getKeystoreDefaultFile(), + WebConfiguration.getKeystoreDefaultAlias(), + WebConfiguration.getKeystoreDefaultPass(), + WebConfiguration.getKeystoreDefaultKeyPass(), + WebConfiguration.getKeystoreDefaultType())) + { + throw new PdfAsWebException("Connector jks[default] is not properly configured"); + } + } + } + } + } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsParameterExtractor.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsParameterExtractor.java index 1ed85e98..a0c407c9 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsParameterExtractor.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsParameterExtractor.java @@ -28,7 +28,8 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.Map; -import javax.servlet.http.HttpServletRequest; +import at.gv.egiz.pdfas.api.ws.PDFASSignParameters.Connector; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -41,7 +42,6 @@ public class PdfAsParameterExtractor { public static final String PARAM_CONNECTOR = "connector"; public static final String PARAM_TRANSACTION_ID = "transactionId"; - public static final String PARAM_CONNECTOR_DEFAULT = "bku"; public static final String PARAM_FORMAT = "format"; public static final String PARAM_HTML = "html"; @@ -63,7 +63,6 @@ public class PdfAsParameterExtractor { public static final String PARAM_VERIFY_LEVEL_OPTION_INT_ONLY = "intOnly"; public static final String PARAM_LOCALE = "locale"; - public static final String PARAM_NUM_BYTES = "num-bytes"; public static final String PARAM_PDF_URL = "pdf-url"; public static final String PARAM_SIG_TYPE = "sig-type"; public static final String PARAM_SIG_TYPE_ALIAS = "sig_type"; @@ -84,12 +83,8 @@ public class PdfAsParameterExtractor { private static final Logger logger = LoggerFactory .getLogger(PdfAsParameterExtractor.class); - public static String getConnector(HttpServletRequest request) { - String connector = (String)request.getAttribute(PARAM_CONNECTOR); - if(connector != null) { - return connector; - } - return PARAM_CONNECTOR_DEFAULT; + public static Connector getConnector(HttpServletRequest request) { + return Connector.fromString((String)request.getAttribute(PARAM_CONNECTOR)); } public static Map<String,String> getDynamicSignatureBlockParameters(HttpServletRequest request) throws Exception { @@ -234,10 +229,6 @@ public class PdfAsParameterExtractor { return null; } - public static String getNumBytes(HttpServletRequest request) { - return (String)request.getAttribute(PARAM_NUM_BYTES); - } - public static String getPdfUrl(HttpServletRequest request) { return (String)request.getAttribute(PARAM_PDF_URL); } @@ -278,7 +269,7 @@ public class PdfAsParameterExtractor { return (String)request.getAttribute(PARAM_SIG_IDX); } - public static String getResonseMode(HttpServletRequest request) { + public static String getResponseMode(HttpServletRequest request) { return (String)request.getAttribute(PARAM_RESPONSE_MODE); } } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/RemotePDFFetcher.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/RemotePDFFetcher.java index 696a3dc1..33811617 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/RemotePDFFetcher.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/RemotePDFFetcher.java @@ -28,6 +28,7 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; +import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; @@ -108,7 +109,7 @@ public class RemotePDFFetcher { if(fetchInfos.length == 3) { String userpass = fetchInfos[1] + ":" + fetchInfos[2]; - String basicAuth = "Basic " + javax.xml.bind.DatatypeConverter.printBase64Binary(userpass.getBytes("UTF-8")); + String basicAuth = "Basic " + jakarta.xml.bind.DatatypeConverter.printBase64Binary(userpass.getBytes(StandardCharsets.UTF_8)); uc.setRequestProperty("Authorization", basicAuth); } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/UrlParameterExtractor.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/UrlParameterExtractor.java index 64043cac..cfb78008 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/UrlParameterExtractor.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/UrlParameterExtractor.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; @@ -20,8 +21,8 @@ public class UrlParameterExtractor { for (String pair : pairs) { int idx = pair.indexOf("="); query_pairs.put( - URLDecoder.decode(pair.substring(0, idx), "UTF-8"), - URLDecoder.decode(pair.substring(idx + 1), "UTF-8")); + URLDecoder.decode(pair.substring(0, idx), StandardCharsets.UTF_8), + URLDecoder.decode(pair.substring(idx + 1), StandardCharsets.UTF_8)); } } return query_pairs; diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/VerifyResultEncoder.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/VerifyResultEncoder.java index 42a4068a..8b477c2e 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/VerifyResultEncoder.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/VerifyResultEncoder.java @@ -3,8 +3,8 @@ package at.gv.egiz.pdfas.web.helper; import java.io.IOException; import java.util.List; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import at.gv.egiz.pdfas.lib.api.verify.VerifyResult; diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/VerifyResultHTMLEncoder.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/VerifyResultHTMLEncoder.java index 590e93a1..3db45370 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/VerifyResultHTMLEncoder.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/VerifyResultHTMLEncoder.java @@ -6,8 +6,8 @@ import java.io.IOException; import java.io.OutputStream; import java.util.List; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import at.gv.egiz.pdfas.lib.api.verify.VerifyResult; diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/VerifyResultJSONEncoder.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/VerifyResultJSONEncoder.java index 43ad3581..b6eac5bc 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/VerifyResultJSONEncoder.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/VerifyResultJSONEncoder.java @@ -7,8 +7,8 @@ import java.io.OutputStream; import java.security.cert.CertificateEncodingException; import java.util.List; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/ExceptionFormatter.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/ExceptionFormatter.java new file mode 100644 index 00000000..8d5f1cea --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/ExceptionFormatter.java @@ -0,0 +1,17 @@ +package at.gv.egiz.pdfas.web.json_api; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.Map; + +@RestControllerAdvice(basePackages = "at.gv.egiz.pdfas.web.json_api") +public class ExceptionFormatter { + @ExceptionHandler(jakarta.xml.ws.WebServiceException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Map<String, Object> mapError(jakarta.xml.ws.WebServiceException e) { + return Map.of("error", e.getMessage()); + } +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/JacksonConfig.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/JacksonConfig.java new file mode 100644 index 00000000..9d316f74 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/JacksonConfig.java @@ -0,0 +1,26 @@ +package at.gv.egiz.pdfas.web.json_api; + +import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer; +import tools.jackson.databind.cfg.EnumFeature; +import tools.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationModule; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** To match how SOAP serializes enum values */ +@Configuration +public class JacksonConfig { + @Bean + public JsonMapperBuilderCustomizer enumsShouldUseToStringToMatchXML() { + return b -> b.enable( + EnumFeature.WRITE_ENUMS_USING_TO_STRING, + EnumFeature.READ_ENUMS_USING_TO_STRING + ); + } + + @Bean + public JsonMapperBuilderCustomizer useJaxbJsonNames() { + return b -> b.addModule( + new JakartaXmlBindAnnotationModule() + ); + } +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/SignController.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/SignController.java new file mode 100644 index 00000000..e20e7ad0 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/SignController.java @@ -0,0 +1,37 @@ +package at.gv.egiz.pdfas.web.json_api; + +import at.gv.egiz.pdfas.api.ws.*; +import at.gv.egiz.pdfas.web.ws.PDFASSigningImpl; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v2/sign") +@AllArgsConstructor +public class SignController { + private final PDFASSigningImpl signingImpl; + + @PostMapping(value = "/single", consumes = "application/json", produces = "application/json") + public PDFASSignResponse signSingle(@RequestBody PDFASSignRequest request) { + return signingImpl.signPDFDokument(request); + } + + @PostMapping(value = "/bulk", consumes = "application/json", produces = "application/json") + public PDFASBulkSignResponse signBulk(@RequestBody PDFASBulkSignRequest request) { + return signingImpl.signPDFDokument(request); + } + + @PostMapping(value = "/multiple", consumes = "application/json", produces = "application/json") + public PdfasSignMultipleResponse signMultiple(@RequestBody PdfasSignMultipleRequest request) { + return signingImpl.signPDFDokument(request); + } + + @PostMapping(value = "/multiple/get-result", consumes = "application/json", produces = "application/json") + public PdfasSignMultipleResponse getMultiple(@RequestBody PdfasGetMultipleRequest request) { + return signingImpl.getSignedDokument(request); + } +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/SoapLogicBridgeBean.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/SoapLogicBridgeBean.java new file mode 100644 index 00000000..b35abfd1 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/SoapLogicBridgeBean.java @@ -0,0 +1,15 @@ +package at.gv.egiz.pdfas.web.json_api; + +import at.gv.egiz.pdfas.web.ws.PDFASSigningImpl; +import at.gv.egiz.pdfas.web.ws.PDFASVerificationImpl; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** Exposes the SOAP service implementations as Spring beans to new code */ +@Configuration +public class SoapLogicBridgeBean { + @Bean + public PDFASSigningImpl signingImplBridge() { return new PDFASSigningImpl(); } + @Bean + public PDFASVerificationImpl verificationImplBridge() { return new PDFASVerificationImpl(); } +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/VerifyController.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/VerifyController.java new file mode 100644 index 00000000..83e287a9 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/json_api/VerifyController.java @@ -0,0 +1,22 @@ +package at.gv.egiz.pdfas.web.json_api; + +import at.gv.egiz.pdfas.api.ws.PDFASVerifyRequest; +import at.gv.egiz.pdfas.api.ws.PDFASVerifyResponse; +import at.gv.egiz.pdfas.web.ws.PDFASVerificationImpl; +import lombok.AllArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v2/verify") +@AllArgsConstructor +public class VerifyController { + private final PDFASVerificationImpl verifyImpl; + + @PostMapping(consumes = "application/json", produces = "application/json") + public PDFASVerifyResponse verify(@RequestBody PDFASVerifyRequest request) { + return verifyImpl.verifyPDFDokument(request); + } +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/DataURLServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/DataURLServlet.java index 50c3b063..f98e5a7b 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/DataURLServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/DataURLServlet.java @@ -25,13 +25,14 @@ package at.gv.egiz.pdfas.web.servlets; import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.annotation.MultipartConfig; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.xml.bind.JAXBElement; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.MultipartConfig; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.xml.bind.JAXBElement; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -78,41 +79,58 @@ public class DataURLServlet extends HttpServlet { } protected void process(HttpServletRequest request, - HttpServletResponse response) throws ServletException, IOException { - try { + HttpServletResponse response) throws ServletException, IOException { + String xmlResponse = null; + try { if(!PdfAsHelper.checkDataUrlAccess(request)) { throw new Exception("No valid dataURL access"); + } PdfAsHelper.setFromDataUrl(request); - String xmlResponse = request.getParameter("XMLResponse"); + xmlResponse = request.getParameter("XMLResponse"); - //System.out.println(xmlResponse); + if (StringUtils.isEmpty(xmlResponse)) { + logger.error("SL response is null or empty"); + throw new PdfAsSecurityLayerException("SL response is null or empty", 9999); + + } JAXBElement<?> jaxbObject = (JAXBElement<?>) SLMarschaller.unmarshalFromString(xmlResponse); if(jaxbObject.getValue() instanceof InfoboxReadResponseType) { InfoboxReadResponseType infoboxReadResponseType = (InfoboxReadResponseType)jaxbObject.getValue(); logger.info("Got InfoboxReadResponseType"); PdfAsHelper.injectCertificate(request, response, PdfAsHelper.getCertificate(infoboxReadResponseType), getServletContext()); + } else if(jaxbObject.getValue() instanceof CreateCMSSignatureResponseType) { CreateCMSSignatureResponseType createCMSSignatureResponseType = (CreateCMSSignatureResponseType)jaxbObject.getValue(); logger.info("Got CreateCMSSignatureResponseType"); PdfAsHelper.injectSignature(request, response, createCMSSignatureResponseType.getCMSSignature(), getServletContext()); + } else if(jaxbObject.getValue() instanceof ErrorResponseType) { ErrorResponseType errorResponseType = (ErrorResponseType)jaxbObject.getValue(); logger.warn("SecurityLayer: " + errorResponseType.getErrorCode() + " " + errorResponseType.getInfo()); - throw new PdfAsSecurityLayerException(errorResponseType.getInfo(), - errorResponseType.getErrorCode()); + throw new PdfAsSecurityLayerException(errorResponseType.getInfo(), errorResponseType.getErrorCode()); + } else { logger.error("Unknown SL response {}", xmlResponse); - throw new PdfAsSecurityLayerException("Unknown SL response", - 9999); + throw new PdfAsSecurityLayerException("Unknown SL response", 9999); + } - } catch (Exception e) { - logger.warn("Error in DataURL Servlet. " , e); - PdfAsHelper.setSessionException(request, response, e.getMessage(), - e); + + } catch (PdfAsSecurityLayerException e) { + PdfAsHelper.setSessionException(request, response, e.getMessage(), e); + PdfAsHelper.gotoError(getServletContext(), request, response); + + } catch (Exception e) { + logger.debug("Receive XML response from VDA: {}", + StringUtils.isNotEmpty(xmlResponse) ? xmlResponse : " ...empty response..."); + logger.warn("General Error in DataURL Servlet. " , e); + + PdfAsHelper.setSessionException(request, response, e.getMessage(), + new PdfAsSecurityLayerException("Invalid SL response", 9999, e)); PdfAsHelper.gotoError(getServletContext(), request, response); + } } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ErrorPage.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ErrorPage.java index 42236f5e..38d883fa 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ErrorPage.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ErrorPage.java @@ -26,13 +26,14 @@ package at.gv.egiz.pdfas.web.servlets; import java.io.IOException; import java.net.URL; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.text.StringEscapeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -141,13 +142,13 @@ public class ErrorPage extends HttpServlet { if (e != null && WebConfiguration.isShowErrorDetails()) { template = template.replace("##CAUSE##", - URLEncoder.encode(e.getMessage(), "UTF-8")); + URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8)); } else { template = template.replace("##CAUSE##", ""); } if (message != null) { template = template.replace("##ERROR##", - URLEncoder.encode(message, "UTF-8")); + URLEncoder.encode(message, StandardCharsets.UTF_8)); } else { template = template.replace("##ERROR##", "Unbekannter Fehler"); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ExternSignServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ExternSignServlet.java index 957614b1..546b07ac 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ExternSignServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ExternSignServlet.java @@ -28,14 +28,15 @@ import java.io.IOException; import java.util.List; import java.util.Map; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import at.gv.egiz.pdfas.web.config.PdfAsWebSpringConfiguration; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; +import lombok.val; +import org.apache.commons.fileupload2.core.DiskFileItem; +import org.apache.commons.fileupload2.core.DiskFileItemFactory; import at.gv.egiz.pdfas.api.processing.CoreSignParams; import at.gv.egiz.pdfas.api.processing.DocumentToSign; @@ -61,16 +62,21 @@ import at.gv.egiz.pdfas.web.stats.StatisticEvent.Source; import at.gv.egiz.pdfas.web.stats.StatisticEvent.Status; import at.gv.egiz.pdfas.web.stats.StatisticFrontend; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletDiskFileUpload; +import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletFileUpload; +import org.apache.commons.io.FilenameUtils; +import org.springframework.boot.web.servlet.ServletRegistration; +import org.springframework.stereotype.Component; /** * Servlet implementation class Sign */ @Slf4j +@Component +@ServletRegistration(urlMappings = "/Sign") public class ExternSignServlet extends HttpServlet { private static final long serialVersionUID = 1L; - - public static final String PDF_AS_WEB_CONF = "pdf-as-web.conf"; private static final String UPLOAD_PDF_DATA = "pdf-file"; private static final String UPLOAD_DIRECTORY = "upload"; @@ -79,15 +85,8 @@ public class ExternSignServlet extends HttpServlet { * Default constructor. */ - public ExternSignServlet(){ - String webconfig = System.getProperty(PDF_AS_WEB_CONF); - - if(webconfig == null) { - log.error("No web configuration provided! Please specify: " + PDF_AS_WEB_CONF); - throw new RuntimeException("No web configuration provided! Please specify: " + PDF_AS_WEB_CONF); - } - - WebConfiguration.configure(webconfig); + public ExternSignServlet(final PdfAsWebSpringConfiguration config) { + WebConfiguration.configure(config.getPdfAsWebConfPath()); PdfAsHelper.init(); try { @@ -176,7 +175,7 @@ public class ExternSignServlet extends HttpServlet { byte[] filecontent = null; // checks if the request actually contains upload file - if (!ServletFileUpload.isMultipartContent(request)) { + if (!JakartaServletFileUpload.isMultipartContent(request)) { // No Uploaded data! if (PdfAsParameterExtractor.getPdfUrl(request) != null) { doGet(request, response); @@ -187,14 +186,14 @@ public class ExternSignServlet extends HttpServlet { } else { // configures upload settings - DiskFileItemFactory factory = new DiskFileItemFactory(); - factory.setSizeThreshold(WebConfiguration.getFilesizeThreshold()); - factory.setRepository(new File(System - .getProperty("java.io.tmpdir"))); + DiskFileItemFactory factory = DiskFileItemFactory.builder() + .setThreshold(WebConfiguration.getFilesizeThreshold()) + .setPath(new File(System.getProperty("java.io.tmpdir")).toPath()) + .get(); - ServletFileUpload upload = new ServletFileUpload(factory); - upload.setFileSizeMax(WebConfiguration.getMaxFilesize()); - upload.setSizeMax(WebConfiguration.getMaxRequestsize()); + val upload = new JakartaServletDiskFileUpload(factory); + upload.setMaxFileSize(WebConfiguration.getMaxFilesize()); + upload.setMaxSize(WebConfiguration.getMaxRequestsize()); // constructs the directory path to store upload file String uploadPath = getServletContext().getRealPath("") @@ -205,9 +204,9 @@ public class ExternSignServlet extends HttpServlet { uploadDir.mkdir(); } - List<?> formItems = upload.parseRequest(request); + List<DiskFileItem> formItems = upload.parseRequest(request); log.debug(formItems.size() + " Items in form data"); - if (formItems.size() < 1) { + if (formItems.isEmpty()) { // No Uploaded data! // Try do get // No Uploaded data! @@ -219,41 +218,35 @@ public class ExternSignServlet extends HttpServlet { "No Signature data defined!"); } } else { - for(int i = 0; i < formItems.size(); i++) { - Object obj = formItems.get(i); - if(obj instanceof FileItem) { - FileItem item = (FileItem) obj; - if(item.getFieldName().equals(UPLOAD_PDF_DATA)) { - filecontent = item.get(); - try { - File f = new File(item.getName()); - String name = f.getName(); - log.debug("Got upload: " + item.getName()); - if(name != null) { - if(!(name.endsWith(".pdf") || name.endsWith(".PDF"))) { - name += ".pdf"; - } - - log.debug("Setting Filename in session: " + name); - PdfAsHelper.setPDFFileName(request, name); - } - } - catch(Throwable e) { - log.warn("In resolving filename", e); - } - if(filecontent.length < 10) { - filecontent = null; - } else { - log.debug("Found pdf Data! Size: " + filecontent.length); - } - } else { - request.setAttribute(item.getFieldName(), item.getString()); - log.debug("Setting " + item.getFieldName() + " = " + item.getString()); - } - } else { - log.debug(obj.getClass().getName() + " - " + obj.toString()); - } - } + for (DiskFileItem item : formItems) { + if (item != null) { + if (item.getFieldName().equals(UPLOAD_PDF_DATA)) { + filecontent = item.getInputStream().readAllBytes(); + try { + val filename = FilenameUtils.getName(item.getName()); + File f = new File(filename); + String name = f.getName(); + log.debug("Got upload: {}", filename); + if (!(name.endsWith(".pdf") || name.endsWith(".PDF"))) { + name += ".pdf"; + } + + log.debug("Setting Filename in session: " + name); + PdfAsHelper.setPDFFileName(request, name); + } catch (Throwable e) { + log.warn("In resolving filename", e); + } + if (filecontent.length < 10) { + filecontent = null; + } else { + log.debug("Found pdf Data! Size: " + filecontent.length); + } + } else { + request.setAttribute(item.getFieldName(), item.getString()); + log.debug("Setting " + item.getFieldName() + " = " + item.getString()); + } + } + } } } @@ -315,13 +308,15 @@ public class ExternSignServlet extends HttpServlet { } // Get Connector - String connector = PdfAsParameterExtractor.getConnector(request); + Connector connector = PdfAsParameterExtractor.getConnector(request); + String keyIdentifier = PdfAsParameterExtractor.getKeyIdentifier(request); + PdfAsHelper.checkConnectorSupported(connector, keyIdentifier); String transactionId = PdfAsParameterExtractor.getTransactionId(request); statisticEvent.setFilesize(pdfData.length); statisticEvent.setProfileId(null); - statisticEvent.setDevice(connector); + statisticEvent.setDevice(connector.toString()); String invokeUrl = PdfAsParameterExtractor.getInvokeURL(request); PdfAsHelper.setInvokeURL(request, response, invokeUrl); @@ -341,7 +336,7 @@ public class ExternSignServlet extends HttpServlet { String locale = PdfAsParameterExtractor.getLocale(request); PdfAsHelper.setLocale(request, response, locale); - String responseMode = PdfAsParameterExtractor.getResonseMode(request); + String responseMode = PdfAsParameterExtractor.getResponseMode(request); PdfAsHelper.setResponseMode(request, response, responseMode); String filename = PdfAsParameterExtractor.getFilename(request); @@ -365,8 +360,8 @@ public class ExternSignServlet extends HttpServlet { CoreSignParams coreParams = new CoreSignParams(); coreParams.setSignatureBlockParameters(dynamicSignatureBlockArguments); - coreParams.setConnector(Connector.fromString(connector)); - coreParams.setKeyIdentifier(PdfAsParameterExtractor.getKeyIdentifier(request)); + coreParams.setConnector(connector); + coreParams.setKeyIdentifier(keyIdentifier); coreParams.setOverrides(PdfAsParameterExtractor.getOverwriteMap(request)); coreParams.setPreprocessor(PdfAsParameterExtractor.getPreProcessorMap(request)); coreParams.setInvokeErrorUrl(errorUrl); @@ -388,67 +383,18 @@ public class ExternSignServlet extends HttpServlet { //IPlainSigner signer; - if (connector.equals("bku") || connector.equals("onlinebku") || connector.equals("mobilebku") - || connector.equals("sl20")) { + if (Connector.isAsynchronous(connector)) { // start asynchronous signature creation - - if(connector.equals("bku")) { - if(WebConfiguration.getLocalBKUURL() == null) { - throw new PdfAsWebException("Invalid connector bku is not supported"); - } - } - if(connector.equals("mobilebku")) { - if(WebConfiguration.getHandyBKUURL() == null) { - throw new PdfAsWebException("Invalid connector mobilebku is not supported"); - } - } - if(connector.equals("onlinebku")) { - if(WebConfiguration.getOnlineBKUURL() == null) { - throw new PdfAsWebException("Invalid connector bku is not supported"); - } - } - if (connector.equals("sl20")) { - if(WebConfiguration.getSecurityLayer20URL() == null) { - throw new PdfAsWebException("Invalid connector bku is not supported"); - } - } PdfAsHelper.setStatisticEvent(request, response, statisticEvent); - // sign document + // sign document PdfAsHelper.startSignature(request, response, getServletContext(), connector, data); return; - } else if (connector.equals("jks") || connector.equals("moa")) { - // start synchronous siganture creation - - if(connector.equals("jks")) { - - String keyIdentifier = PdfAsParameterExtractor.getKeyIdentifier(request); - - boolean ksEnabled = false; - - if (keyIdentifier != null) { - ksEnabled = WebConfiguration.getKeystoreEnabled(keyIdentifier); - } else { - ksEnabled = WebConfiguration.getKeystoreDefaultEnabled(); - } - - if (!ksEnabled) { - if(keyIdentifier != null) { - throw new PdfAsWebException("JKS connector [" + keyIdentifier + "] disabled or not existing."); - } else { - throw new PdfAsWebException("DEFAULT JKS connector disabled."); - } - } - } - - if(connector.equals("moa")) { - if(!WebConfiguration.getMOASSEnabled()) { - throw new PdfAsWebException("Invalid connector moa is not supported"); - } - } + } else { + // start synchronous signature creation // sign document PdfasSignResponse pdfSignedData = PdfAsHelper.synchronousServerSignature(data); @@ -466,9 +412,6 @@ public class ExternSignServlet extends HttpServlet { PdfAsHelper.gotoProvidePdf(getServletContext(), request, response); return; - } else { - throw new PdfAsWebException("Invalid connector (bku | moa | jks)"); - } } } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java index d5ef2079..a9b282a2 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/JSONAPIServlet.java @@ -1,14 +1,15 @@ package at.gv.egiz.pdfas.web.servlets; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; @@ -65,7 +66,7 @@ public class JSONAPIServlet extends HttpServlet { return; } - String jsonString = IOUtils.toString(req.getInputStream(), "UTF-8"); + String jsonString = IOUtils.toString(req.getInputStream(), StandardCharsets.UTF_8); logger.debug("Reading json String {}", jsonString); @@ -86,7 +87,7 @@ public class JSONAPIServlet extends HttpServlet { String profile = jsonObject.has(JSON_PROFILE) ? jsonObject.getString(JSON_PROFILE) : null; String position = jsonObject.has(JSON_POSITION) ? jsonObject.getString(JSON_POSITION) : null; - String connector = jsonObject.getString(JSON_CONNECTOR); + Connector connector = Connector.fromString(jsonObject.getString(JSON_CONNECTOR)); String input = jsonObject.getString(JSON_INPUT); String requestID = jsonObject.has(JSON_REQUEST_ID) ? jsonObject.getString(JSON_REQUEST_ID) : null; @@ -105,40 +106,14 @@ public class JSONAPIServlet extends HttpServlet { try { - if(connector == null) { - throw new ServletException( - "Invalid connector value!"); - } - - PDFASSignParameters.Connector connectorEnum = null; - - if(PDFASSignParameters.Connector.MOA.equalsName(connector)) { - connectorEnum = PDFASSignParameters.Connector.MOA; - } else if(PDFASSignParameters.Connector.JKS.equalsName(connector)) { - connectorEnum = PDFASSignParameters.Connector.JKS; - } else if(PDFASSignParameters.Connector.BKU.equalsName(connector)) { - connectorEnum = PDFASSignParameters.Connector.BKU; - } else if(PDFASSignParameters.Connector.MOBILEBKU.equalsName(connector)) { - connectorEnum = PDFASSignParameters.Connector.MOBILEBKU; - } else if(PDFASSignParameters.Connector.ONLINEBKU.equalsName(connector)) { - connectorEnum = PDFASSignParameters.Connector.ONLINEBKU; - } else if(PDFASSignParameters.Connector.SECLAYER20.equalsName(connector)) { - connectorEnum = PDFASSignParameters.Connector.SECLAYER20; - } - - if(connectorEnum == null) { - throw new ServletException( - "Invalid connector value!"); - } - - // TODO: check connector is enabled! + PdfAsHelper.checkConnectorSupported(connector, null); statisticEvent.setFilesize(inputDocument.length); statisticEvent.setProfileId(profile); - statisticEvent.setDevice(connector); + statisticEvent.setDevice(connector.toString()); PDFASSignParameters parameters = new PDFASSignParameters(); - parameters.setConnector(connectorEnum); + parameters.setConnector(connector); parameters.setPosition(position); parameters.setProfile(profile); @@ -155,8 +130,8 @@ public class JSONAPIServlet extends HttpServlet { signatureBlockParametersMap.put(values[0], values[1]); } } - }catch(Exception e){ - e.printStackTrace(); + } catch(Exception e) { + logger.warn("Failed to process JSON sbp parameter", e); } @@ -166,7 +141,7 @@ public class JSONAPIServlet extends HttpServlet { CoreSignParams coreParams = new CoreSignParams(); coreParams.setSignatureBlockParameters(signatureBlockParametersMap); - coreParams.setConnector(Connector.fromString(connector)); + coreParams.setConnector(connector); data.setCoreParams(coreParams); DocumentToSign document = new DocumentToSign(); @@ -177,8 +152,7 @@ public class JSONAPIServlet extends HttpServlet { - if (PDFASSignParameters.Connector.MOA.equals(connectorEnum) - || PDFASSignParameters.Connector.JKS.equals(connectorEnum)) { + if (!Connector.isAsynchronous(connector)) { // Plain server based signatures!! @@ -235,36 +209,8 @@ public class JSONAPIServlet extends HttpServlet { // start asynchronous signature creation - if (PDFASSignParameters.Connector.BKU.equals(connectorEnum)) { - if (WebConfiguration.getLocalBKUURL() == null) { - throw new PdfAsWebException( - "Invalid connector bku is not supported"); - } - } - - if (PDFASSignParameters.Connector.ONLINEBKU.equals(connectorEnum)) { - if (WebConfiguration.getLocalBKUURL() == null) { - throw new PdfAsWebException( - "Invalid connector onlinebku is not supported"); - } - } - - if (PDFASSignParameters.Connector.MOBILEBKU.equals(connectorEnum)) { - if (WebConfiguration.getLocalBKUURL() == null) { - throw new PdfAsWebException( - "Invalid connector mobilebku is not supported"); - } - } - - if (PDFASSignParameters.Connector.SECLAYER20.equals(connectorEnum)) { - if (WebConfiguration.getSecurityLayer20URL() == null) { - throw new PdfAsWebException( - "Invalid connector mobilebku is not supported"); - } - } - - PdfAsHelper.startSignatureJson(request, response, getServletContext(), - connectorEnum.toString(), data); + PdfAsHelper.startSignatureJson(request, response, getServletContext(), + connector, data); JSONStartResponse jsonStartResponse = PdfAsHelper.startJsonProcess(request, response, getServletContext()); @@ -279,7 +225,7 @@ public class JSONAPIServlet extends HttpServlet { } response.setContentType("application/json"); - IOUtils.write(jsonResponse.toString(), response.getOutputStream(), "UTF-8"); + IOUtils.write(jsonResponse.toString(), response.getOutputStream(), StandardCharsets.UTF_8); } catch (Throwable e) { diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java index 96d02f16..e7556569 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java @@ -34,10 +34,10 @@ import java.util.zip.Deflater; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import at.gv.egiz.pdfas.api.processing.PdfasSignResponse; import at.gv.egiz.pdfas.api.processing.SignedDocument; diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFSignatureCertificateData.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFSignatureCertificateData.java index e4465e77..bf3f3f85 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFSignatureCertificateData.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFSignatureCertificateData.java @@ -28,10 +28,10 @@ import java.io.OutputStream; import java.security.cert.CertificateEncodingException; import java.util.List; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -95,15 +95,16 @@ public class PDFSignatureCertificateData extends HttpServlet { "Content-Disposition", "inline;filename=cert_" + id + ".cer"); response.setContentType("application/pkix-cert"); + response.setHeader("X-Content-Type-Options", "nosniff"); OutputStream os = response.getOutputStream(); os.write(res.getSignerCertificate().getEncoded()); os.close(); } else { - logger.warn("Verification CERT not found! for id " + request.getParameter(SIGN_ID) + " in session " + request.getSession().getId()); + logger.warn("Verification CERT not found! for id {} in session {}", request.getParameter(SIGN_ID), request.getSession().getId()); response.sendError(HttpServletResponse.SC_NOT_FOUND); } } catch (NumberFormatException e) { - logger.warn("Verification CERT not found! for id " + request.getParameter(SIGN_ID) + " in session " + request.getSession().getId()); + logger.warn("Verification CERT not found! for id {} in session {}", request.getParameter(SIGN_ID), request.getSession().getId()); response.sendError(HttpServletResponse.SC_NOT_FOUND); } catch (PdfAsException e) { logger.warn("Verification CERT not found:", e); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFSignatureData.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFSignatureData.java index e493f4ae..14f13f05 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFSignatureData.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFSignatureData.java @@ -27,10 +27,10 @@ import java.io.IOException; import java.io.OutputStream; import java.util.List; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -94,15 +94,16 @@ public class PDFSignatureData extends HttpServlet { "Content-Disposition", "inline;filename=signed_data_" + id + ".pdf"); response.setContentType("application/pdf"); + response.setHeader("X-Content-Type-Options", "nosniff"); OutputStream os = response.getOutputStream(); - os.write(res.getSignatureData()); + os.write(res.getSignatureData().getBaseData()); os.close(); } else { - logger.warn("Verification DATA not found! for id " + request.getParameter(SIGN_ID) + " in session " + request.getSession().getId()); + logger.warn("Verification DATA not found! for id {} in session {}", request.getParameter(SIGN_ID), request.getSession().getId()); response.sendError(HttpServletResponse.SC_NOT_FOUND); } } catch (NumberFormatException e) { - logger.warn("Verification DATA not found! for id " + request.getParameter(SIGN_ID) + " in session " + request.getSession().getId()); + logger.warn("Verification DATA not found! for id {} in session {}", request.getParameter(SIGN_ID), request.getSession().getId()); response.sendError(HttpServletResponse.SC_NOT_FOUND); } catch (PdfAsException e) { logger.warn("Verification DATA not found:", e); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFURLData.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFURLData.java index d4112cad..63172175 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFURLData.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFURLData.java @@ -6,11 +6,11 @@ import at.gv.egiz.pdfas.lib.api.StatusRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; import java.io.IOException; import java.io.OutputStream; diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PlaceholderGeneratorServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PlaceholderGeneratorServlet.java index b07293b1..388c7e9d 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PlaceholderGeneratorServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PlaceholderGeneratorServlet.java @@ -10,10 +10,10 @@ import java.io.IOException; import java.io.InputStream; import javax.imageio.ImageIO; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.http.HttpStatus; import org.slf4j.Logger; diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ProvidePDFServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ProvidePDFServlet.java index 47469eb2..f6b42024 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ProvidePDFServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ProvidePDFServlet.java @@ -26,15 +26,16 @@ package at.gv.egiz.pdfas.web.servlets; import java.io.IOException; import java.net.URL; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.List; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.text.StringEscapeUtils; import at.gv.egiz.pdfas.api.processing.SignedDocument; import at.gv.egiz.pdfas.common.exceptions.PdfAsException; @@ -214,8 +215,8 @@ public class ProvidePDFServlet extends HttpServlet { template = template.replace("##TARGET##", StringEscapeUtils.escapeHtml4(target)); template = template.replace("##PDFURL##", - URLEncoder.encode(PdfAsHelper.generatePdfURL(request, response), - "UTF-8")); + URLEncoder.encode(PdfAsHelper.generatePdfURL(request, response), + StandardCharsets.UTF_8)); response.setContentType("text/html"); response.getWriter().write(template); response.getWriter().close(); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ReloadServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ReloadServlet.java index 84e86634..89d33e80 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ReloadServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/ReloadServlet.java @@ -3,17 +3,26 @@ package at.gv.egiz.pdfas.web.servlets; import java.io.IOException; import java.io.OutputStream; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import at.gv.egiz.pdfas.web.config.PdfAsWebSpringConfiguration; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import at.gv.egiz.pdfas.web.config.WebConfiguration; import at.gv.egiz.pdfas.web.helper.PdfAsHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.ServletRegistration; +import org.springframework.stereotype.Component; +@Slf4j +@Component +@ServletRegistration(urlMappings = "/Reload") public class ReloadServlet extends HttpServlet { /** @@ -21,17 +30,12 @@ public class ReloadServlet extends HttpServlet { */ private static final long serialVersionUID = 6108555300743896727L; - private static final Logger logger = LoggerFactory - .getLogger(ReloadServlet.class); - - public static final String PDF_AS_WEB_CONF = "pdf-as-web.conf"; public static final String PARAM_PASSWD = "PASSWD"; - /** - * @see HttpServlet#HttpServlet() - */ - public ReloadServlet() { + private final PdfAsWebSpringConfiguration config; + public ReloadServlet(final PdfAsWebSpringConfiguration config) { super(); + this.config = config; } /** @@ -42,15 +46,15 @@ public class ReloadServlet extends HttpServlet { HttpServletResponse response) throws ServletException, IOException { if(!WebConfiguration.getReloadEnabled()) { - logger.info("Reload Servlet disabled. " + request.getRemoteAddr() + " tried to call it"); + log.info("Reload Servlet disabled. " + request.getRemoteAddr() + " tried to call it"); response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.setContentLength(0); return; } - logger.info("Called Reload Servlet from: " + request.getRemoteAddr()); + log.info("Called Reload Servlet from: " + request.getRemoteAddr()); - logger.info("Checking Password!"); + log.info("Checking Password!"); String pwd = request.getParameter(PARAM_PASSWD); @@ -66,17 +70,11 @@ public class ReloadServlet extends HttpServlet { return; } - String webconfig = System.getProperty(PDF_AS_WEB_CONF); - - if(webconfig == null) { - logger.error("No web configuration provided! Please specify: " + PDF_AS_WEB_CONF); - throw new RuntimeException("No web configuration provided! Please specify: " + PDF_AS_WEB_CONF); - } - + String webconfig = config.getPdfAsWebConfPath(); WebConfiguration.configure(webconfig); PdfAsHelper.reloadConfig(); - logger.info("Reloaded!"); + log.info("Reloaded!"); StringBuilder sb = new StringBuilder(); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SLDataURLServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SLDataURLServlet.java index 55946afb..9f144f84 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SLDataURLServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SLDataURLServlet.java @@ -1,16 +1,17 @@ package at.gv.egiz.pdfas.web.servlets; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Base64; import java.util.List; -import javax.servlet.ServletException; -import javax.servlet.annotation.MultipartConfig; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.MultipartConfig; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.jose4j.base64url.Base64Url; @@ -81,7 +82,7 @@ public class SLDataURLServlet extends HttpServlet { String sl20Result = request.getParameter(SL20Constants.PARAM_SL20_REQ_COMMAND_PARAM); if (StringUtils.isEmpty(sl20Result)) { //Workaround for SIC Handy-Signature, because it sends result in InputStream - String isReqInput = StreamUtils.readStream(request.getInputStream(), "UTF-8"); + String isReqInput = StreamUtils.readStream(request.getInputStream(), StandardCharsets.UTF_8); if (StringUtils.isNotEmpty(isReqInput)) { logger.info("Use SIC Handy-Signature work-around!"); sl20Result = isReqInput.substring("slcommand=".length()); @@ -93,17 +94,16 @@ public class SLDataURLServlet extends HttpServlet { } - logger.trace("Received SL2.0 command: " + sl20Result); + logger.trace("Received SL2.0 command: {}", sl20Result); //parse SL2.0 command/result into JSON try { - JsonParser jsonParser = new JsonParser(); - JsonElement sl20Req = jsonParser.parse(Base64Url.decodeToUtf8String(sl20Result)); + JsonElement sl20Req = JsonParser.parseString(Base64Url.decodeToUtf8String(sl20Result)); sl20ReqObj = sl20Req.getAsJsonObject(); } catch (JsonSyntaxException e) { logger.warn("SL2.0 command or result is NOT valid JSON.", e); - logger.debug("SL2.0 msg: " + sl20Result); + logger.debug("SL2.0 msg: {}", sl20Result); throw new SL20Exception("sl20.02", e); } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SoapServiceServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SoapServiceServlet.java index ca005abe..8b97d7f7 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SoapServiceServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/SoapServiceServlet.java @@ -1,15 +1,24 @@ package at.gv.egiz.pdfas.web.servlets; -import javax.servlet.ServletConfig; -import javax.xml.ws.Endpoint; +import jakarta.servlet.ServletConfig; +import jakarta.xml.ws.Endpoint; +import lombok.extern.slf4j.Slf4j; +import lombok.val; import org.apache.cxf.Bus; import org.apache.cxf.BusFactory; +import org.apache.cxf.jaxws.EndpointImpl; +import org.apache.cxf.logging.FaultListener; +import org.apache.cxf.message.Message; import org.apache.cxf.transport.servlet.CXFNonSpringServlet; import at.gv.egiz.pdfas.web.ws.PDFASSigningImpl; import at.gv.egiz.pdfas.web.ws.PDFASVerificationImpl; +import java.util.LinkedList; +import java.util.List; + +@Slf4j public class SoapServiceServlet extends CXFNonSpringServlet { /** @@ -17,24 +26,47 @@ public class SoapServiceServlet extends CXFNonSpringServlet { */ private static final long serialVersionUID = -8903883276191902043L; + @Slf4j + public static class Faults implements FaultListener { + @Override + public boolean faultOccurred(Exception exception, String description, Message message) { + String operation = "-"; + if (message != null && message.getExchange() != null) { + val boi = message.getExchange().getBindingOperationInfo(); + if (boi != null && boi.getName() != null) { + operation = boi.getName().toString(); + } + } + + log.error("Unhandled SOAP fault in operation {}: {}", operation, description, exception); + return false; + } + } + + private final List<EndpointImpl> endpoints = new LinkedList<>(); @Override protected void loadBus(ServletConfig sc) { - super.loadBus(sc); - // You could add the endpoint publish codes here - Bus bus = this.getBus(); - BusFactory.setDefaultBus(bus); - Endpoint signEp = Endpoint.publish("/wssign", new PDFASSigningImpl()); - /* - * SOAPBinding signBinding = (SOAPBinding)signEp.getBinding(); - signBinding.setMTOMEnabled(true); - */ - - Endpoint verifyEp = Endpoint.publish("/wsverify", new PDFASVerificationImpl()); - /* - SOAPBinding verifyBinding = (SOAPBinding)verifyEp.getBinding(); - verifyBinding.setMTOMEnabled(true); - */ - + Bus bus = BusFactory.newInstance(BusFactory.DEFAULT_BUS_FACTORY).createBus(); + bus.setProperty(FaultListener.class.getName(), new Faults()); + setBus(bus); + + val signingEndpoint = new EndpointImpl(bus, new PDFASSigningImpl()); + endpoints.add(signingEndpoint); + signingEndpoint.publish("/wssign"); + + val verificationEndpoint = new EndpointImpl(bus, new PDFASVerificationImpl()); + endpoints.add(verificationEndpoint); + verificationEndpoint.publish("/wsverify"); + } + + @Override + public void destroyBus() { + endpoints.forEach(p -> { + try { p.close(); } catch (Exception e) { log.warn("Failed to close endpoint cleanly", e); } + }); + endpoints.clear(); + BusFactory.clearDefaultBusForAnyThread(getBus()); + super.destroyBus(); } } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java index d7a3d3c6..51cc5573 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java @@ -25,10 +25,10 @@ package at.gv.egiz.pdfas.web.servlets; import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import at.gv.egiz.pdfas.api.processing.PdfasSignRequest; import at.gv.egiz.pdfas.api.ws.PDFASSignParameters.Connector; @@ -42,6 +42,7 @@ import at.gv.egiz.pdfas.web.helper.PdfAsHelper; import at.gv.egiz.pdfas.web.stats.StatisticEvent; import at.gv.egiz.pdfas.web.store.RequestStore; import lombok.extern.slf4j.Slf4j; +import lombok.val; @Slf4j public class UIEntryPointServlet extends HttpServlet { @@ -76,20 +77,21 @@ public class UIEntryPointServlet extends HttpServlet { throw new PdfAsStoreException("Wrong Parameters"); } - PdfasSignRequest pdfAsRequest = RequestStore.getInstance() + val storeEntry = RequestStore.getInstance() .fetchStoreEntry(storeId); - if (pdfAsRequest == null) { + if (storeEntry == null) { throw new PdfAsStoreException("Invalid " + REQUEST_ID_PARAM + " value"); } - StatisticEvent statisticEvent = RequestStore.getInstance() - .fetchStatisticEntry(storeId); + val pdfAsRequest = storeEntry.getFirst(); + val statisticEvent = storeEntry.getSecond(); PdfAsHelper.setStatisticEvent(req, resp, statisticEvent); Connector connector = pdfAsRequest.getCoreParams().getConnector(); + PdfAsHelper.checkConnectorSupported(connector, null); String invokeUrl = pdfAsRequest.getCoreParams().getInvokeUrl(); PdfAsHelper.setInvokeURL(req, resp, invokeUrl); @@ -125,41 +127,10 @@ public class UIEntryPointServlet extends HttpServlet { log.debug("Starting signature creation with: " + connector); // IPlainSigner signer; - if (connector.equals(Connector.BKU) - || connector.equals(Connector.ONLINEBKU) - || connector.equals(Connector.MOBILEBKU) - || connector.equals(Connector.SECLAYER20)) { + if (Connector.isAsynchronous(connector)) { // start asynchronous signature creation - - if (connector.equals(Connector.BKU)) { - if (WebConfiguration.getLocalBKUURL() == null) { - throw new PdfAsWebException( - "Invalid connector bku is not supported"); - } - } - - if (connector.equals(Connector.ONLINEBKU)) { - if (WebConfiguration.getOnlineBKUURL() == null) { - throw new PdfAsWebException( - "Invalid connector onlinebku is not supported"); - } - } - - if (connector.equals(Connector.MOBILEBKU)) { - if (WebConfiguration.getHandyBKUURL() == null) { - throw new PdfAsWebException( - "Invalid connector mobilebku is not supported"); - } - } - - if (connector.equals(Connector.SECLAYER20)) { - if (WebConfiguration.getSecurityLayer20URL() == null) { - throw new PdfAsWebException( - "Invalid connector mobilebku is not supported"); - } - } - PdfAsHelper.startSignature(req, resp, getServletContext(), connector.toString(), pdfAsRequest); + PdfAsHelper.startSignature(req, resp, getServletContext(), connector, pdfAsRequest); } else { throw new PdfAsWebException("Invalid connector (" diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/VerifyServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/VerifyServlet.java index 003a4a73..2334597a 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/VerifyServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/VerifyServlet.java @@ -27,14 +27,18 @@ import java.io.File; import java.io.IOException; import java.util.List; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; +import lombok.val; +import org.apache.commons.fileupload2.core.DiskFileItem; +import org.apache.commons.fileupload2.core.FileItem; +import org.apache.commons.fileupload2.core.DiskFileItemFactory; +import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletDiskFileUpload; +import org.apache.commons.fileupload2.jakarta.servlet6.JakartaServletFileUpload; +import org.apache.commons.io.FilenameUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,10 +58,15 @@ import at.gv.egiz.pdfas.web.stats.StatisticEvent.Operation; import at.gv.egiz.pdfas.web.stats.StatisticEvent.Source; import at.gv.egiz.pdfas.web.stats.StatisticEvent.Status; import at.gv.egiz.pdfas.web.stats.StatisticFrontend; +import org.springframework.boot.web.servlet.ServletRegistration; +import org.springframework.stereotype.Component; /** * Servlet implementation class VerifyServlet */ + +@Component +@ServletRegistration(urlMappings = "/Verify") public class VerifyServlet extends HttpServlet { private static final long serialVersionUID = 1L; @@ -159,7 +168,7 @@ public class VerifyServlet extends HttpServlet { byte[] filecontent = null; // checks if the request actually contains upload file - if (!ServletFileUpload.isMultipartContent(request)) { + if (!JakartaServletFileUpload.isMultipartContent(request)) { // No Uploaded data! if (PdfAsParameterExtractor.getPdfUrl(request) != null) { doGet(request, response); @@ -169,14 +178,14 @@ public class VerifyServlet extends HttpServlet { } } else { // configures upload settings - DiskFileItemFactory factory = new DiskFileItemFactory(); - factory.setSizeThreshold(THRESHOLD_SIZE); - factory.setRepository(new File(System - .getProperty("java.io.tmpdir"))); + DiskFileItemFactory factory = DiskFileItemFactory.builder() + .setThreshold(THRESHOLD_SIZE) + .setPath(new File(System.getProperty("java.io.tmpdir")).toPath()) + .get(); - ServletFileUpload upload = new ServletFileUpload(factory); - upload.setFileSizeMax(MAX_FILE_SIZE); - upload.setSizeMax(MAX_REQUEST_SIZE); + val upload = new JakartaServletDiskFileUpload(factory); + upload.setMaxFileSize(MAX_FILE_SIZE); + upload.setMaxSize(MAX_REQUEST_SIZE); // constructs the directory path to store upload file String uploadPath = getServletContext().getRealPath("") @@ -187,9 +196,9 @@ public class VerifyServlet extends HttpServlet { uploadDir.mkdir(); } - List<?> formItems = upload.parseRequest(request); + List<DiskFileItem> formItems = upload.parseRequest(request); logger.debug(formItems.size() + " Items in form data"); - if (formItems.size() < 1) { + if (formItems.isEmpty()) { // No Uploaded data! // Try do get // No Uploaded data! @@ -201,48 +210,41 @@ public class VerifyServlet extends HttpServlet { "No Signature data defined!"); } } else { - for (int i = 0; i < formItems.size(); i++) { - Object obj = formItems.get(i); - if (obj instanceof FileItem) { - FileItem item = (FileItem) obj; - if (item.getFieldName().equals(UPLOAD_PDF_DATA)) { - filecontent = item.get(); - try { - File f = new File(item.getName()); - String name = f.getName(); - logger.debug("Got upload: " - + item.getName()); - if (name != null) { - if (!(name.endsWith(".pdf") || name - .endsWith(".PDF"))) { - name += ".pdf"; - } + for (DiskFileItem item : formItems) { + if (item != null) { + if (item.getFieldName().equals(UPLOAD_PDF_DATA)) { + filecontent = item.getInputStream().readAllBytes(); + try { + val filename = FilenameUtils.getName(item.getName()); + File f = new File(filename); + String name = f.getName(); + logger.debug("Got upload: {}", filename); + if (!(name.endsWith(".pdf") || name + .endsWith(".PDF"))) { + name += ".pdf"; + } - logger.debug("Setting Filename in session: " - + name); - PdfAsHelper.setPDFFileName(request, - name); - } - } catch (Throwable e) { - logger.warn("In resolving filename", e); - } - if (filecontent.length < 10) { - filecontent = null; - } else { - logger.debug("Found pdf Data! Size: " - + filecontent.length); - } - } else { - request.setAttribute(item.getFieldName(), - item.getString()); - logger.debug("Setting " + item.getFieldName() - + " = " + item.getString()); - } - } else { - logger.debug(obj.getClass().getName() + " - " - + obj.toString()); - } - } + logger.debug("Setting Filename in session: " + + name); + PdfAsHelper.setPDFFileName(request, + name); + } catch (Throwable e) { + logger.warn("In resolving filename", e); + } + if (filecontent.length < 10) { + filecontent = null; + } else { + logger.debug("Found pdf Data! Size: " + + filecontent.length); + } + } else { + request.setAttribute(item.getFieldName(), + item.getString()); + logger.debug("Setting " + item.getFieldName() + + " = " + item.getString()); + } + } + } } } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/VisBlockServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/VisBlockServlet.java index b49264f7..eda6eef0 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/VisBlockServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/VisBlockServlet.java @@ -4,10 +4,10 @@ import java.io.IOException; import java.io.OutputStream; import java.security.cert.CertificateException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,6 +72,7 @@ public class VisBlockServlet extends HttpServlet { response.setHeader("Content-Disposition", "inline;filename=" + profile + "_" + resolution + ".png"); response.setContentType("image/png"); + response.setHeader("X-Content-Type-Options", "nosniff"); OutputStream os = response.getOutputStream(); os.write(imageData); os.close(); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/JsonSecurityUtils.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/JsonSecurityUtils.java index bf37b290..7a4d24ae 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/JsonSecurityUtils.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/JsonSecurityUtils.java @@ -181,10 +181,10 @@ public class JsonSecurityUtils implements IJOSETools{ jws.setCompactSerialization(serializedContent); //set security constrains - jws.setAlgorithmConstraints(new AlgorithmConstraints(ConstraintType.WHITELIST, - SL20Constants.SL20_ALGORITHM_WHITELIST_SIGNING.toArray(new String[SL20Constants.SL20_ALGORITHM_WHITELIST_SIGNING.size()]))); + jws.setAlgorithmConstraints(new AlgorithmConstraints(ConstraintType.PERMIT, + SL20Constants.SL20_ALGORITHM_WHITELIST_SIGNING.toArray(new String[0]))); - //load signinc certs + //load signing certs Key selectedKey = null; List<X509Certificate> x5cCerts = jws.getCertificateChainHeaderValue(); String x5t256 = jws.getX509CertSha256ThumbprintHeaderValue(); @@ -232,7 +232,7 @@ public class JsonSecurityUtils implements IJOSETools{ //load payLoad logger.debug("SL2.0 commando signature validation sucessfull"); - JsonElement sl20Req = new JsonParser().parse(jws.getPayload()); + JsonElement sl20Req = JsonParser.parseString(jws.getPayload()); return new VerificationResult(sl20Req.getAsJsonObject(), null, valid) ; @@ -252,11 +252,11 @@ public class JsonSecurityUtils implements IJOSETools{ //set security constrains receiverJwe.setAlgorithmConstraints( - new AlgorithmConstraints(ConstraintType.WHITELIST, - SL20Constants.SL20_ALGORITHM_WHITELIST_KEYENCRYPTION.toArray(new String[SL20Constants.SL20_ALGORITHM_WHITELIST_KEYENCRYPTION.size()]))); + new AlgorithmConstraints(ConstraintType.PERMIT, + SL20Constants.SL20_ALGORITHM_WHITELIST_KEYENCRYPTION.toArray(new String[0]))); receiverJwe.setContentEncryptionAlgorithmConstraints( - new AlgorithmConstraints(ConstraintType.WHITELIST, - SL20Constants.SL20_ALGORITHM_WHITELIST_ENCRYPTION.toArray(new String[SL20Constants.SL20_ALGORITHM_WHITELIST_ENCRYPTION.size()]))); + new AlgorithmConstraints(ConstraintType.PERMIT, + SL20Constants.SL20_ALGORITHM_WHITELIST_ENCRYPTION.toArray(new String[0]))); //set payload receiverJwe.setCompactSerialization(compactSerialization); @@ -295,7 +295,7 @@ public class JsonSecurityUtils implements IJOSETools{ //decrypt payload - return new JsonParser().parse(receiverJwe.getPlaintextString()); + return JsonParser.parseString(receiverJwe.getPlaintextString()); } catch (JoseException e) { logger.warn("SL2.0 result decryption FAILED", e); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/SL20HttpBindingUtils.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/SL20HttpBindingUtils.java index e43ebfcf..d59f649a 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/SL20HttpBindingUtils.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/sl20/SL20HttpBindingUtils.java @@ -3,9 +3,10 @@ package at.gv.egiz.pdfas.web.sl20; import java.io.IOException; import java.io.StringWriter; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.ContentType; @@ -27,7 +28,7 @@ public class SL20HttpBindingUtils { log.debug("Client request containts 'native client' header ... "); StringWriter writer = new StringWriter(); writer.write(sl20Forward.toString()); - final byte[] content = writer.toString().getBytes("UTF-8"); + final byte[] content = writer.toString().getBytes(StandardCharsets.UTF_8); response.setStatus(HttpServletResponse.SC_OK); response.setContentLength(content.length); response.setContentType(ContentType.APPLICATION_JSON.toString()); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/stats/StatisticFrontend.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/stats/StatisticFrontend.java index f006be54..e78e63ab 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/stats/StatisticFrontend.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/stats/StatisticFrontend.java @@ -23,23 +23,16 @@ public class StatisticFrontend implements StatisticBackend { private List<StatisticBackend> statisticBackends = new ArrayList<StatisticBackend>(); private StatisticFrontend() { - Iterator<StatisticBackend> statisticIterator = backendLoader.iterator(); List<String> enabledBackends = WebConfiguration.getStatisticBackends(); if (enabledBackends == null) { - logger.info("No statitistic backends configured using all available."); + logger.info("No statistic backends configured, using all available."); } else { - Iterator<String> enabledBackendsIterator = enabledBackends - .iterator(); logger.info("Allowing the following statistic backends:"); - while (enabledBackendsIterator.hasNext()) { - logger.info(" - {}", enabledBackendsIterator.next()); - } + enabledBackends.forEach(it -> logger.info(" - {}", it)); } - while (statisticIterator.hasNext()) { - StatisticBackend statisticBackend = statisticIterator.next(); - + for (StatisticBackend statisticBackend : backendLoader) { if (enabledBackends == null || enabledBackends.contains(statisticBackend.getName())) { logger.info("adding Statistic Logger {} [{}]", statisticBackend @@ -54,22 +47,8 @@ public class StatisticFrontend implements StatisticBackend { } if (enabledBackends != null) { - Iterator<String> enabledBackendsIterator = enabledBackends - .iterator(); - while (enabledBackendsIterator.hasNext()) { - String enabledBackend = enabledBackendsIterator.next(); - statisticIterator = statisticBackends.iterator(); - boolean found = false; - while (statisticIterator.hasNext()) { - StatisticBackend statisticBackend = statisticIterator - .next(); - if (statisticBackend.getName().equals(enabledBackend)) { - found = true; - break; - } - } - - if (!found) { + for (String enabledBackend : enabledBackends) { + if (statisticBackends.stream().noneMatch(it -> it.getName().equals(enabledBackend))) { logger.warn( "Failed to load statistic backend {}. Not in classpath?", enabledBackend); @@ -103,13 +82,7 @@ public class StatisticFrontend implements StatisticBackend { return; } - Iterator<StatisticBackend> statisticBackendIterator = statisticBackends - .iterator(); - - while (statisticBackendIterator.hasNext()) { - StatisticBackend statisticBackend = statisticBackendIterator.next(); - statisticBackend.storeEvent(statisticEvent); - } + statisticBackends.forEach(statisticBackend -> statisticBackend.storeEvent(statisticEvent)); } } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/stats/impl/StatisticMicrometerBackend.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/stats/impl/StatisticMicrometerBackend.java new file mode 100644 index 00000000..8b8fb489 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/stats/impl/StatisticMicrometerBackend.java @@ -0,0 +1,75 @@ +package at.gv.egiz.pdfas.web.stats.impl; + +import at.gv.egiz.pdfas.web.stats.StatisticBackend; +import at.gv.egiz.pdfas.web.stats.StatisticEvent; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.jspecify.annotations.NonNull; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; + +@Slf4j +public class StatisticMicrometerBackend implements StatisticBackend { + /** bridge between ServiceLoader component and Boot's beans */ + @Component + public static class SpringContextProxy { + public static MeterRegistry meterRegistry; + SpringContextProxy(MeterRegistry registry) { meterRegistry = registry; } + } + public static final String NAME = "StatisticMicrometerBackend"; + @Override public String getName() { return NAME; } + + @Override + public void storeEvent(StatisticEvent e) { + if (e == null) return; + + MeterRegistry registry = SpringContextProxy.meterRegistry; + if (registry == null) { + log.warn("Spring MeterRegistry not available, skipped micrometer metric logging"); + return; + } + + Tags baseTags = Tags.of( + "operation", safeName(e.getOperation(), v -> v.getName()), + "status", safeName(e.getStatus(), v -> v.getName()), + "source", safeName(e.getSource(), v -> v.getName()), + "device", safeString(e.getDevice()), + "profile", safeString(e.getProfileId()) + ); + + Timer.builder("pdfas_requests") + .description("Duration of PDF-AS operations") + .tags(baseTags) + .publishPercentileHistogram() + .register(registry) + .record(Math.max(0, e.getDuration()), TimeUnit.MILLISECONDS); + + if (e.getStatus() == StatisticEvent.Status.ERROR) { + String whichException = safeName(e.getException(), it -> it.getClass().getSimpleName()); + Counter.builder("pdfas_errors") + .description("Failed PDF-AS operations") + .tags(baseTags.and("exception", whichException)) + .register(registry) + .increment(); + } + } + + private static @NonNull String safeString(String str) { + return ((str == null) || str.isBlank()) ? "unknown" : str; + } + + private static <T> @NonNull String safeName(T v, @NonNull Function<T, String> op) { + return safeString((v != null) ? op.apply(v) : null); + } +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/IRequestStore.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/IRequestStore.java index 643d3ea0..fdcded6c 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/IRequestStore.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/IRequestStore.java @@ -26,11 +26,11 @@ package at.gv.egiz.pdfas.web.store; import at.gv.egiz.pdfas.api.processing.PdfasSignRequest; import at.gv.egiz.pdfas.api.processing.PdfasSignResponse; import at.gv.egiz.pdfas.web.stats.StatisticEvent; +import kotlin.Pair; public interface IRequestStore { - public StatisticEvent fetchStatisticEntry(String id); public String createNewStoreEntry(PdfasSignRequest request, StatisticEvent event); - public PdfasSignRequest fetchStoreEntry(String id); + public Pair<PdfasSignRequest, StatisticEvent> fetchStoreEntry(String id); public String createNewResponseEntry(PdfasSignResponse response); public PdfasSignResponse fetchStoreResponse(String id); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/InMemoryRequestStore.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/InMemoryRequestStore.java index 6ab58ce0..378714e4 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/InMemoryRequestStore.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/InMemoryRequestStore.java @@ -26,6 +26,8 @@ package at.gv.egiz.pdfas.web.store; import java.util.Map; import java.util.UUID; +import kotlin.Pair; +import lombok.val; import org.apache.commons.collections4.map.PassiveExpiringMap; import at.gv.egiz.pdfas.api.processing.PdfasSignRequest; @@ -37,40 +39,27 @@ public class InMemoryRequestStore implements IRequestStore { // expires after 10 minutes private static final long DEFAULT_EXPIRATION = 10 * 60 * 1000; - private Map<String, PdfasSignRequest> reqStore = new PassiveExpiringMap<>(DEFAULT_EXPIRATION); + private Map<String, Pair<PdfasSignRequest, StatisticEvent>> reqStore = new PassiveExpiringMap<>(DEFAULT_EXPIRATION); private Map<String, PdfasSignResponse> respStore = new PassiveExpiringMap<>(DEFAULT_EXPIRATION); - private Map<String, StatisticEvent> statEvents = new PassiveExpiringMap<>(DEFAULT_EXPIRATION); public InMemoryRequestStore() { } - + + @Override public String createNewStoreEntry(PdfasSignRequest request, StatisticEvent event) { UUID id = UUID.randomUUID(); String sid = id.toString(); - this.reqStore.put(sid, request); - this.statEvents.put(sid, event); + this.reqStore.put(sid, new Pair<>(request, event)); return sid; - } - public StatisticEvent fetchStatisticEntry(String id) { - if(statEvents.containsKey(id)) { - StatisticEvent event = statEvents.get(id); - statEvents.remove(id); - return event; - - } - - return null; - } - - public PdfasSignRequest fetchStoreEntry(String id) { + @Override + public Pair<PdfasSignRequest, StatisticEvent> fetchStoreEntry(String id) { if(reqStore.containsKey(id)) { - PdfasSignRequest request = reqStore.get(id); + val storeEntry = reqStore.get(id); reqStore.remove(id); - return request; - + return storeEntry; } return null; diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/RequestStore.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/RequestStore.java index a5e961ef..e39e9399 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/RequestStore.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/RequestStore.java @@ -42,15 +42,15 @@ public class RequestStore { logger.info("Using Request Store: " + storeClass); Class<?> clazz = Class.forName(storeClass); - Object store = clazz.newInstance(); + Object store = clazz.getDeclaredConstructor().newInstance(); if(store instanceof IRequestStore) { instance = (IRequestStore)store; } else { - throw new PdfAsStoreException("Failed to instanciate Request Store from " + storeClass); + throw new PdfAsStoreException("Failed to instantiate Request Store from " + storeClass); } } catch (Throwable e) { - e.printStackTrace(); - throw new PdfAsStoreException("Failed to instanciate Request Store", e); + logger.error("Failed to instantiate Request Store", e); + throw new PdfAsStoreException("Failed to instantiate Request Store", e); } } return instance; diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/web_xml_bridges/FilterBridge.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/web_xml_bridges/FilterBridge.java new file mode 100644 index 00000000..ee9d0cb4 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/web_xml_bridges/FilterBridge.java @@ -0,0 +1,52 @@ +package at.gv.egiz.pdfas.web.web_xml_bridges; + +import org.apache.catalina.filters.SetCharacterEncodingFilter; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.thetransactioncompany.cors.CORSFilter; + +import at.gv.egiz.pdfas.web.filter.ExceptionCatchFilter; +import at.gv.egiz.pdfas.web.filter.UserAgentFilter; +import jakarta.servlet.Filter; +import lombok.val; + +/** Takes the old web.xml filter mappings and exposes them to Spring Boot */ +@Configuration +public class FilterBridge { + @Bean + public FilterRegistrationBean<Filter> setCharacterEncodingFilter() { + val reg = new FilterRegistrationBean<Filter>(new SetCharacterEncodingFilter()); + reg.addUrlPatterns("/*"); + reg.addInitParameter("encoding", "UTF-8"); + reg.setOrder(1); + return reg; + } + + @Bean + public FilterRegistrationBean<Filter> exceptionCatchFilter() { + val reg = new FilterRegistrationBean<Filter>(new ExceptionCatchFilter()); + reg.addUrlPatterns("/*"); + reg.addInitParameter("statelessServlets", "/actuator,/placeholder,/visblock"); + reg.setOrder(2); + return reg; + } + + @Bean + public FilterRegistrationBean<Filter> userAgentFilter() { + val reg = new FilterRegistrationBean<Filter>(new UserAgentFilter()); + reg.addUrlPatterns("/*"); + reg.setOrder(3); + return reg; + } + + @Bean + public FilterRegistrationBean<Filter> cors() { + val reg = new FilterRegistrationBean<Filter>(new CORSFilter()); + reg.addUrlPatterns("/*"); + reg.addInitParameter("cors.allowOrigin", "*"); + reg.setOrder(4); + return reg; + } +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/web_xml_bridges/ServletBridge.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/web_xml_bridges/ServletBridge.java new file mode 100644 index 00000000..3337a417 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/web_xml_bridges/ServletBridge.java @@ -0,0 +1,107 @@ +package at.gv.egiz.pdfas.web.web_xml_bridges; + +import at.gv.egiz.pdfas.web.servlets.*; +import jakarta.servlet.Servlet; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** Takes the old web.xml servlet mappings and exposes them to Spring Boot */ +@Configuration +public class ServletBridge { + @Bean + public ServletRegistrationBean<Servlet> cxfServlet() { + return new ServletRegistrationBean<>( + /** from <servlet> */ new SoapServiceServlet(), + /** from <servlet-mapping> */ "/services/*" + ); + } + + @Bean + public ServletRegistrationBean<Servlet> visBlockServlet() { + return new ServletRegistrationBean<>( + new VisBlockServlet(), + "/visblock" + ); + } + + @Bean + public ServletRegistrationBean<Servlet> providePDF() { + return new ServletRegistrationBean<>( + new ProvidePDFServlet(), + "/ProvidePDF" + ); + } + + @Bean + public ServletRegistrationBean<Servlet> dataURLServlet() { + return new ServletRegistrationBean<>( + new DataURLServlet(), + "/DataURL" + ); + } + + @Bean + public ServletRegistrationBean<Servlet> slDataURLServlet() { + return new ServletRegistrationBean<>( + new SLDataURLServlet(), + "/DataURLSL20" + ); + } + + @Bean + public ServletRegistrationBean<Servlet> pdfData() { + return new ServletRegistrationBean<>( + new PDFData(), + "/PDFData" + ); + } + + @Bean + public ServletRegistrationBean<Servlet> errorPage() { + return new ServletRegistrationBean<>( + new ErrorPage(), + "/ErrorPage" + ); + } + + @Bean + public ServletRegistrationBean<Servlet> pdfVerifyData() { + return new ServletRegistrationBean<>( + new PDFSignatureData(), + "/signData" + ); + } + + @Bean + public ServletRegistrationBean<Servlet> pdfVerifyCert() { + return new ServletRegistrationBean<>( + new PDFSignatureCertificateData(), + "/signCert" + ); + } + + @Bean + public ServletRegistrationBean<Servlet> uiEntryPointServlet() { + return new ServletRegistrationBean<>( + new UIEntryPointServlet(), + "/userentry" + ); + } + + @Bean + public ServletRegistrationBean<Servlet> qrPlaceholderGenerator() { + return new ServletRegistrationBean<>( + new PlaceholderGeneratorServlet(), + "/placeholder" + ); + } + + @Bean + public ServletRegistrationBean<Servlet> jsonAPIServlet() { + return new ServletRegistrationBean<>( + new JSONAPIServlet(), + "/api/v1/sign" + ); + } +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/web_xml_bridges/WelcomeFileBridge.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/web_xml_bridges/WelcomeFileBridge.java new file mode 100644 index 00000000..6fcf47fa --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/web_xml_bridges/WelcomeFileBridge.java @@ -0,0 +1,12 @@ +package at.gv.egiz.pdfas.web.web_xml_bridges; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class WelcomeFileBridge { + @GetMapping("/") + public String welcomeFile() { + return "forward:/index.jsp"; + } +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/ws/PDFASSigningImpl.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/ws/PDFASSigningImpl.java index dce3e34c..83e59706 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/ws/PDFASSigningImpl.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/ws/PDFASSigningImpl.java @@ -28,10 +28,13 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import javax.jws.WebService; -import javax.xml.ws.WebServiceException; -import javax.xml.ws.soap.MTOM; +import at.gv.egiz.pdfas.lib.api.PdfAs; +import at.gv.egiz.pdfas.lib.impl.ErrorExtractor; +import jakarta.jws.WebService; +import jakarta.xml.ws.WebServiceException; +import jakarta.xml.ws.soap.MTOM; +import lombok.val; import org.apache.commons.lang3.StringUtils; import at.gv.egiz.pdfas.api.processing.CoreSignParams; @@ -88,28 +91,31 @@ public class PDFASSigningImpl implements PDFASSigning { log.warn("SOAP Sign Request is null!"); return null; } + if (request.getParameters() == null) { + throw new WebServiceException("SOAP Sign Request parameters are missing"); + } // map request into internal data-structure final PdfasSignRequest internalReq = buildOperationRequest(request); + val connector = internalReq.getCoreParams().getConnector(); + val keyIdentifier = internalReq.getCoreParams().getKeyIdentifier(); + final StatisticEvent statisticEvent = new StatisticEvent(); statisticEvent.setSource(Source.SOAP); statisticEvent.setOperation(Operation.SIGN); statisticEvent.setUserAgent(UserAgentFilter.getUserAgent()); statisticEvent.setProfileId(request.getParameters().getProfile()); - statisticEvent.setDevice(request.getParameters().getConnector().toString()); + if (connector != null) { + statisticEvent.setDevice(connector.toString()); + } statisticEvent.setStartNow(); PDFASSignResponse response = new PDFASSignResponse(); try { - if (request.getParameters().getConnector() == null) { - throw new WebServiceException( - "Invalid connector value!"); - } + PdfAsHelper.checkConnectorSupported(connector, keyIdentifier); - if (request.getParameters().getConnector().equals(Connector.MOA) - || request.getParameters().getConnector() - .equals(Connector.JKS)) { + if (!Connector.isAsynchronous(connector)) { // perform technical signing process final PdfasSignResponse internalResp = PdfAsHelper.synchronousServerSignature(internalReq); @@ -142,20 +148,20 @@ public class PDFASSigningImpl implements PDFASSigning { } } catch (final Throwable e) { + val pdfAsError = ErrorExtractor.searchPdfAsError(e, null); + statisticEvent.setStatus(Status.ERROR); statisticEvent.setException(e); - if (e instanceof PDFASError) { - statisticEvent.setErrorCode(((PDFASError) e).getCode()); - } + statisticEvent.setErrorCode(pdfAsError.getCode()); statisticEvent.setEndNow(); statisticEvent.setTimestampNow(); StatisticFrontend.getInstance().storeEvent(statisticEvent); statisticEvent.setLogged(true); log.warn("Error in Soap Service", e); + response.setErrorCode(pdfAsError.getCode()); if (e.getCause() != null) { response.setError(e.getCause().getMessage()); - } else { response.setError(e.getMessage()); @@ -210,24 +216,23 @@ public class PDFASSigningImpl implements PDFASSigning { // map request into internal data-structure final PdfasSignRequest internalReq = buildOperationRequest(request); + val connector = internalReq.getCoreParams().getConnector(); + val keyIdentifier = internalReq.getCoreParams().getKeyIdentifier(); final StatisticEvent statisticEvent = new StatisticEvent(); statisticEvent.setSource(Source.SOAP); statisticEvent.setOperation(Operation.SIGNBULK); statisticEvent.setUserAgent(UserAgentFilter.getUserAgent()); - statisticEvent.setDevice(internalReq.getCoreParams().getConnector().toString()); + if (connector != null) { + statisticEvent.setDevice(connector.toString()); + } statisticEvent.setStartNow(); PdfasSignMultipleResponse response = new PdfasSignMultipleResponse(); try { - if (internalReq.getCoreParams().getConnector() == null) { - throw new WebServiceException( - "Invalid connector value!"); - } + PdfAsHelper.checkConnectorSupported(connector, keyIdentifier); - if (internalReq.getCoreParams().getConnector().equals(Connector.MOA) - || internalReq.getCoreParams().getConnector() - .equals(Connector.JKS)) { + if (!Connector.isAsynchronous(connector)) { // perform technical signing process final PdfasSignResponse internalResp = PdfAsHelper.synchronousServerSignature(internalReq); @@ -370,6 +375,9 @@ public class PDFASSigningImpl implements PDFASSigning { coreParams.setTransactionId(request.getTransactionId()); data.setCoreParams(coreParams); + if (request.getInput() == null || request.getInput().isEmpty()) { + throw new WebServiceException("SignMultipleRequest contains no input documents"); + } request.getInput().forEach(el -> { final DocumentToSign document = new DocumentToSign(); document.setInputData(el.getInputData()); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/ws/PDFASVerificationImpl.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/ws/PDFASVerificationImpl.java index b1bca4ba..1afd0c2d 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/ws/PDFASVerificationImpl.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/ws/PDFASVerificationImpl.java @@ -16,9 +16,9 @@ import iaik.x509.X509Certificate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.jws.WebService; -import javax.xml.ws.WebServiceException; -import javax.xml.ws.soap.MTOM; +import jakarta.jws.WebService; +import jakarta.xml.ws.WebServiceException; +import jakarta.xml.ws.soap.MTOM; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -69,7 +69,11 @@ public class PDFASVerificationImpl implements PDFASVerification { } statisticEvent.setFilesize(request.getInputData().length); statisticEvent.setProfileId(null); - statisticEvent.setDevice(request.getVerificationLevel().toString()); + statisticEvent.setDevice( + (lvl == SignatureVerificationLevel.FULL_VERIFICATION ? + VerificationLevel.FULL_CERT_PATH : + VerificationLevel.INTEGRITY_ONLY) + .toString()); List<VerifyResult> results = PdfAsHelper.synchronousVerify( request.getInputData(), sigIdx, lvl, preProcessor); |
