Skip to content

Commit

Permalink
Merge pull request #298 from solver-it-sro/AG-71/refactor-endpint-exc…
Browse files Browse the repository at this point in the history
…eptions

AG-71 Refactor and handle sign endpoints
  • Loading branch information
celuchmarek authored Oct 18, 2023
2 parents 4c77dbb + d06ae39 commit ccd20ea
Show file tree
Hide file tree
Showing 21 changed files with 150 additions and 132 deletions.
7 changes: 7 additions & 0 deletions src/main/java/digital/slovensko/autogram/core/Autogram.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import digital.slovensko.autogram.core.errors.AutogramException;
import digital.slovensko.autogram.core.errors.BatchConflictException;
import digital.slovensko.autogram.core.errors.BatchNotStartedException;
import digital.slovensko.autogram.core.errors.ResponseNetworkErrorException;
import digital.slovensko.autogram.core.errors.UnrecognizedException;
import digital.slovensko.autogram.core.visualization.DocumentVisualizationBuilder;
import digital.slovensko.autogram.core.visualization.UnsupportedVisualization;
Expand Down Expand Up @@ -112,6 +113,8 @@ public void sign(SigningJob job, SigningKey signingKey) {
onSigningFailed(AutogramException.createFromDSSException(e));
} catch (IllegalArgumentException e) {
onSigningFailed(AutogramException.createFromIllegalArgumentException(e));
} catch (ResponseNetworkErrorException e) {
onSigningFailed(e, job);
} catch (Exception e) {
onSigningFailed(new UnrecognizedException(e));
}
Expand Down Expand Up @@ -215,6 +218,10 @@ public void onDocumentBatchSaved(BatchUiResult result) {
ui.onUIThreadDo(() -> ui.onDocumentBatchSaved(result));
}

public void onSigningFailed(AutogramException e, SigningJob job) {
ui.onUIThreadDo(() -> ui.onSigningFailed(e, job));
}

public void onSigningFailed(AutogramException e) {
ui.onUIThreadDo(() -> ui.onSigningFailed(e));
}
Expand Down
16 changes: 2 additions & 14 deletions src/main/java/digital/slovensko/autogram/core/SigningJob.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import eu.europa.esig.dss.asic.cades.signature.ASiCWithCAdESService;
import eu.europa.esig.dss.asic.xades.signature.ASiCWithXAdESService;
import eu.europa.esig.dss.cades.signature.CAdESService;
import eu.europa.esig.dss.enumerations.MimeType;
import eu.europa.esig.dss.enumerations.SignatureLevel;
import eu.europa.esig.dss.model.CommonDocument;
import eu.europa.esig.dss.model.DSSDocument;
Expand All @@ -22,21 +21,11 @@ public class SigningJob {
private final Responder responder;
private final CommonDocument document;
private final SigningParameters parameters;
private final MimeType transformationOutputMimeTypeForXdc;

public SigningJob(CommonDocument document, SigningParameters parameters, Responder responder,
MimeType transformationOutputMimeTypeForXdc) {
this.document = document;
this.parameters = parameters;
this.responder = responder;
this.transformationOutputMimeTypeForXdc = transformationOutputMimeTypeForXdc;
}

public SigningJob(CommonDocument document, SigningParameters parameters, Responder responder) {
this.document = document;
this.parameters = parameters;
this.responder = responder;
this.transformationOutputMimeTypeForXdc = null;
}

public CommonDocument getDocument() {
Expand All @@ -55,7 +44,7 @@ private boolean isDocumentXDC() {
return document.getMimeType().equals(AutogramMimeType.XML_DATACONTAINER);
}

public void signWithKeyAndRespond(SigningKey key) throws InterruptedException {
public void signWithKeyAndRespond(SigningKey key) throws InterruptedException, AutogramException {

Logging.log("Signing Job: " + this.hashCode() + " file " + getDocument().getName());
boolean isContainer = getParameters().getContainer() != null;
Expand Down Expand Up @@ -92,8 +81,7 @@ private DSSDocument signDocumentAsCAdeS(SigningKey key) {
private DSSDocument signDocumentAsAsiCWithXAdeS(SigningKey key) {
DSSDocument doc = getDocument();
if (getParameters().shouldCreateDatacontainer() && !isDocumentXDC()) {
var transformer = XDCTransformer.buildFromSigningParameters(getParameters(),
transformationOutputMimeTypeForXdc);
var transformer = XDCTransformer.buildFromSigningParameters(getParameters());
doc = transformer.transform(doc);
doc.setMimeType(AutogramMimeType.XML_DATACONTAINER);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
package digital.slovensko.autogram.core;

import java.io.IOException;
import java.io.StringReader;

import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import digital.slovensko.autogram.core.errors.TransformationParsingErrorException;
import eu.europa.esig.dss.asic.cades.ASiCWithCAdESSignatureParameters;
import eu.europa.esig.dss.asic.xades.ASiCWithXAdESSignatureParameters;
import eu.europa.esig.dss.cades.CAdESSignatureParameters;
Expand Down Expand Up @@ -203,4 +212,33 @@ public int getVisualizationWidth() {
return (visualizationWidth > 0) ? visualizationWidth : 640;
}

public String extractTransformationOutputMimeTypeString() throws TransformationParsingErrorException {
if (transformation == null)
return "TXT";

var method = "";
try {
var builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
var document = builderFactory.newDocumentBuilder()
.parse(new InputSource(new StringReader(transformation)));
var elem = document.getDocumentElement();
var outputElements = elem.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Transform", "output");
if (outputElements.getLength() == 0)
throw new TransformationParsingErrorException("Failed to parse transformation. Missing output element");

method = outputElements.item(0).getAttributes().getNamedItem("method").getNodeValue();

} catch (SAXException | IOException | ParserConfigurationException e) {
throw new TransformationParsingErrorException("Failed to parse transformation");
}

if (method.equals("html"))
return "HTML";

if (method.equals("text"))
return "TXT";

throw new TransformationParsingErrorException("Unsupported transformation output method: " + method);
}
}
23 changes: 6 additions & 17 deletions src/main/java/digital/slovensko/autogram/core/XDCTransformer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import digital.slovensko.autogram.core.errors.InvalidXMLException;
import eu.europa.esig.dss.enumerations.DigestAlgorithm;
import eu.europa.esig.dss.enumerations.MimeType;
import eu.europa.esig.dss.enumerations.MimeTypeEnum;
import eu.europa.esig.dss.model.DSSDocument;
import eu.europa.esig.dss.model.InMemoryDocument;
import eu.europa.esig.dss.spi.DSSUtils;
Expand Down Expand Up @@ -31,41 +29,32 @@
import java.util.Base64;

public class XDCTransformer {

public enum DestinationMediaType {
TXT, HTML, XHTML
}

private final String identifierUri;
private final String identifierVersion;
private final String xsdSchema;
private final String xsltSchema;
private final String canonicalizationMethod;
private final String containerXmlns;
private final DigestAlgorithm digestAlgorithm;
private final DestinationMediaType mediaDestinationTypeDescription;
private final String mediaDestinationTypeDescription;

private Document document;
private String documentXmlns;

/**
*
*
* @param sp
* @param visualizationMimeType - this is because getting transformation mime type can throw
* @return
*/
public static XDCTransformer buildFromSigningParameters(SigningParameters sp, MimeType visualizationMimeType) {
var mediaType = DestinationMediaType.TXT;
if (visualizationMimeType != null && visualizationMimeType.equals(MimeTypeEnum.HTML))
mediaType = DestinationMediaType.HTML;

public static XDCTransformer buildFromSigningParameters(SigningParameters sp) {
return new XDCTransformer(sp.getIdentifier(), sp.getSchema(), sp.getTransformation(), sp.getContainerXmlns(),
sp.getPropertiesCanonicalization(), sp.getDigestAlgorithm(), mediaType);
sp.getPropertiesCanonicalization(), sp.getDigestAlgorithm(), sp.extractTransformationOutputMimeTypeString());
}

private XDCTransformer(String identifier, String xsdSchema, String xsltSchema, String containerXmlns,
String canonicalizationMethod, DigestAlgorithm digestAlgorithm,
DestinationMediaType mediaDestinationTypeDescription) {
String mediaDestinationTypeDescription) {
int lastSlashIndex = identifier.lastIndexOf("/");
if (lastSlashIndex == -1)
throw new RuntimeException("Identifier contains no slash: " + identifier);
Expand Down Expand Up @@ -241,7 +230,7 @@ private Element createUsedPresentationSchemaReference() {
element.setAttribute("DigestMethod", toNamespacedString(digestAlgorithm));
element.setAttribute("DigestValue", computeDigest(xsltSchema));
element.setAttribute("ContentType", "application/xslt+xml");
element.setAttribute("MediaDestinationTypeDescription", mediaDestinationTypeDescription.name());
element.setAttribute("MediaDestinationTypeDescription", mediaDestinationTypeDescription);
element.setAttribute("Language", "sk");
element.setTextContent(buildXSLTReference());

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package digital.slovensko.autogram.core.errors;

public class ResponseNetworkErrorException extends AutogramException {
public ResponseNetworkErrorException(String message, Exception e) {
super("Nastala chyba", "Nepodarilo sa poslať odpoveď externej aplikácii", message, e);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package digital.slovensko.autogram.core.errors;

public class TransformationParsingErrorException extends AutogramException {
public TransformationParsingErrorException(String message) {
super("Nastala chyba", "Nastala chyba pri čítaní XSLT transformácie", message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
Expand All @@ -28,7 +27,6 @@
import digital.slovensko.autogram.core.SigningParameters;
import digital.slovensko.autogram.core.errors.AutogramException;
import digital.slovensko.autogram.util.AsicContainerUtils;
import eu.europa.esig.dss.enumerations.MimeType;
import eu.europa.esig.dss.enumerations.MimeTypeEnum;
import eu.europa.esig.dss.model.CommonDocument;

Expand All @@ -48,11 +46,10 @@ public static Visualization fromJob(SigningJob job) throws IOException, ParserCo
}

private Visualization build(SigningJob job) throws IOException, ParserConfigurationException, TransformerException, SAXException {
var transformationOutputMime = getTransformationOutputMimeType(getTransformation());
return createVisualization(job, transformationOutputMime);
return createVisualization(job);
}

private Visualization createVisualization(SigningJob job, MimeType transformationOutputMimeType)
private Visualization createVisualization(SigningJob job)
throws IOException, ParserConfigurationException, SAXException, TransformerException {

var documentToDisplay = this.document;
Expand All @@ -66,10 +63,11 @@ private Visualization createVisualization(SigningJob job, MimeType transformatio

if (isTranformationAvailable(getTransformation()) && isDocumentSupportingTransformation(documentToDisplay)) {

var transformationOutputMimeType = parameters.extractTransformationOutputMimeTypeString();
// Applying transformation
if (transformationOutputMimeType.equals(MimeTypeEnum.HTML)) {
if (transformationOutputMimeType.equals("HTML")) {
return new HTMLVisualization(transform(documentToDisplay), job);
} else if (transformationOutputMimeType.equals(MimeTypeEnum.TEXT)) {
} else if (transformationOutputMimeType.equals("TEXT")) {
return new PlainTextVisualization(transform(documentToDisplay), job);
} else {
return new UnsupportedVisualization(job);
Expand Down Expand Up @@ -118,28 +116,6 @@ private boolean isDocumentXDC(DSSDocument documentToDisplay) {
return documentToDisplay.getMimeType().equals(AutogramMimeType.XML_DATACONTAINER);
}

public static MimeType getTransformationOutputMimeType(String transformation)
throws SAXException, IOException, ParserConfigurationException {
if (transformation == null)
return null;

var builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
var document = builderFactory.newDocumentBuilder()
.parse(new InputSource(new StringReader(transformation)));
var elem = document.getDocumentElement();
var outputElements = elem.getElementsByTagNameNS("http://www.w3.org/1999/XSL/Transform", "output");
var method = outputElements.item(0).getAttributes().getNamedItem("method").getNodeValue();

if (method.equals("html"))
return MimeTypeEnum.HTML;

if (method.equals("text"))
return MimeTypeEnum.TEXT;

throw new RuntimeException("Unsupported transformation output method: " + method);
}

/**
* Transform document (XML) using transformation (XSLT)
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,17 @@
public class DocumentationEndpoint implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
boolean isYaml = exchange.getRequestURI().getPath().endsWith(".yml");
var isYaml = exchange.getRequestURI().getPath().endsWith(".yml");
var mimeType = isYaml ? "text/yaml" : "text/html";
var filename = isYaml ? "server.yml" : "index.html";
var stream = getClass().getResourceAsStream(filename);

exchange.getResponseHeaders().set("Content-Type", mimeType);

// automatically closes both streams
try (var stream = getClass().getResourceAsStream(filename);
var responseStream = exchange.getResponseBody()) {
try (exchange) {
exchange.getResponseHeaders().set("Content-Type", mimeType);
exchange.sendResponseHeaders(200, 0);

requireNonNull(stream).transferTo(responseStream);
} catch (Exception e) {
throw new RuntimeException(e);
requireNonNull(stream).transferTo(exchange.getResponseBody());
}

stream.close();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.google.gson.Gson;
import com.sun.net.httpserver.HttpExchange;

import digital.slovensko.autogram.core.errors.ResponseNetworkErrorException;
import digital.slovensko.autogram.server.dto.ErrorResponse;
import digital.slovensko.autogram.server.errors.EmptyBodyException;
import java.io.IOException;
Expand All @@ -17,7 +19,7 @@ public static void respondWithError(ErrorResponse error, HttpExchange exchange)
exchange.getResponseBody().write(gson.toJson(error.getBody()).getBytes());
exchange.getResponseBody().close();
} catch (IOException e) {
throw new RuntimeException(e);
throw new ResponseNetworkErrorException("Externá aplikácia nečakala na odpoveď", e);
}
}

Expand All @@ -28,7 +30,7 @@ public static void respondWith(Object response, HttpExchange exchange) {
exchange.getResponseBody().write(gson.toJson(response).getBytes());
exchange.getResponseBody().close();
} catch (IOException e) {
throw new RuntimeException(e);
throw new ResponseNetworkErrorException("Externá aplikácia nečakala na odpoveď", e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,10 @@ public void handle(HttpExchange exchange) throws IOException {
var response = new InfoResponse(Main.getVersionString(), getStatus());
var gson = new Gson();

try {
try (exchange) {
exchange.getResponseHeaders().add("Content-Type", "application/json");
exchange.sendResponseHeaders(200, 0);
exchange.getResponseBody().write(gson.toJson(response).getBytes());
exchange.getResponseBody().close();
} catch (Exception e) {
e.printStackTrace();
return; // TODO: handle error
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import digital.slovensko.autogram.core.Responder;
import digital.slovensko.autogram.core.SignedDocument;
import digital.slovensko.autogram.core.errors.AutogramException;
import digital.slovensko.autogram.core.errors.ResponseNetworkErrorException;
import digital.slovensko.autogram.server.dto.ErrorResponse;
import digital.slovensko.autogram.server.dto.SignResponse;

Expand All @@ -18,15 +19,15 @@ public ServerResponder(HttpExchange exchange) {
}

@Override
public void onDocumentSigned(SignedDocument signedDocument) {
public void onDocumentSigned(SignedDocument signedDocument) throws AutogramException {
var signer = signedDocument.getCertificate().getSubject().getPrincipal().toString();
var issuer = signedDocument.getCertificate().getIssuer().getPrincipal().toString();

try {
var b64document = Base64.getEncoder().encodeToString(signedDocument.getDocument().openStream().readAllBytes());
EndpointUtils.respondWith(new SignResponse(b64document, signer, issuer), exchange);
} catch (IOException e) {
throw new RuntimeException(e);
throw new ResponseNetworkErrorException("Externá aplikácia nečakala na odpoveď", e);
}
}

Expand Down
Loading

0 comments on commit ccd20ea

Please sign in to comment.