diff --git a/Server/src/main/java/org/openas2/cmd/processor/restapi/ApiResource.java b/Server/src/main/java/org/openas2/cmd/processor/restapi/ApiResource.java index a5bf91a2..1d8b16d9 100644 --- a/Server/src/main/java/org/openas2/cmd/processor/restapi/ApiResource.java +++ b/Server/src/main/java/org/openas2/cmd/processor/restapi/ApiResource.java @@ -5,48 +5,69 @@ */ package org.openas2.cmd.processor.restapi; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; -import org.openas2.cert.AliasedCertificateFactory; -import org.openas2.cert.CertificateFactory; -import org.openas2.cmd.CommandResult; -import org.openas2.cmd.processor.RestCommandProcessor; import jakarta.annotation.security.RolesAllowed; import jakarta.ws.rs.Consumes; - import jakarta.ws.rs.DefaultValue; - - +import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; +import jakarta.ws.rs.HEAD; import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.PathParam; import jakarta.ws.rs.POST; +import jakarta.ws.rs.Produces; import jakarta.ws.rs.PUT; -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.HEAD; - - -import jakarta.ws.rs.PathParam; - +import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.Context; - -import jakarta.ws.rs.core.MultivaluedMap; -import jakarta.ws.rs.core.Request; -import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedMap; + import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.StringWriter; import java.security.cert.Certificate; import java.security.cert.X509Certificate; + import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathFactory; + +import org.glassfish.grizzly.http.server.Request; + +import org.openas2.cert.AliasedCertificateFactory; +import org.openas2.cert.CertificateFactory; +import org.openas2.cmd.CommandResult; +import org.openas2.cmd.processor.RestCommandProcessor; +import org.openas2.Session; +import org.openas2.util.Properties; + import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + + + /** * @author javier */ @@ -70,12 +91,11 @@ public static void setProcessor(RestCommandProcessor aProcessor) { private static RestCommandProcessor processor; @Context UriInfo ui; - @Context Request request; private final ObjectMapper mapper; - + public ApiResource() { - + mapper = new ObjectMapper(); // enable pretty printing mapper.enable(SerializationFeature.INDENT_OUTPUT); @@ -220,6 +240,99 @@ public Response headCommand(@PathParam("param") String command) { return Response.status(200).build(); } + @GET + @RolesAllowed({"ADMIN"}) + @Path("/getPropertyList") + @Produces(MediaType.APPLICATION_JSON) + public Response getPropertyList(@Context Request request) { + if (!request.isSecure() && !isLocalhost(request)) { + return Response.status(Response.Status.FORBIDDEN) + .entity("{\"error\":\"SSL/TLS required\"}") + .type(MediaType.APPLICATION_JSON) + .build(); + } + Map result = new HashMap<>(); + try { + result = (Map) Properties.getProperties(); + }catch(Exception ex) { + LoggerFactory.getLogger(ApiResource.class.getName()).error(ex.getMessage(), ex); + throw ex; + } + ObjectMapper om = new ObjectMapper(); + try { + String js = om.writeValueAsString(result); + return Response.ok(js, MediaType.APPLICATION_JSON).build(); + } catch (JsonProcessingException e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("error").type(MediaType.APPLICATION_JSON).build(); + } + } + + @GET + @RolesAllowed({"ADMIN"}) + @Path("/getXml") + @Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON}) + public Response getXml(@Context Request request, @QueryParam("filename") String filename, @QueryParam("xpath") String xpathExpression){ + + if (!request.isSecure() && !isLocalhost(request)) { + return Response.status(Response.Status.FORBIDDEN) + .entity("{\"error\":\"SSL/TLS required\"}") + .type(MediaType.APPLICATION_JSON) + .build(); + } + + Session session = getProcessor().getSession(); + String filePath = session.getBaseDirectory() + "/" + filename; + try { + NodeList nodeList = getNodes(filePath, xpathExpression); + DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document resultDocument = db.newDocument(); + for (int i = 0; i < nodeList.getLength(); i++) { + Node importedNode = resultDocument.importNode(nodeList.item(i), true); + resultDocument.appendChild(importedNode); + } + StringWriter stringWriter = new StringWriter(); // Convert the XML document to a string + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + transformer.transform(new DOMSource(resultDocument), new StreamResult(stringWriter)); + String xmlContent = stringWriter.toString(); + return Response.ok(xmlContent, MediaType.APPLICATION_XML).build(); + } catch (Exception exception) { + return Response.serverError().entity("error").type(MediaType.APPLICATION_JSON).build(); + } + } + + private static boolean isLocalhost(Request request) { + boolean isLocalhost = request.getRemoteAddr().equals("127.0.0.1") || request.getRemoteAddr().equals("::1"); + return isLocalhost; + } + + private NodeList getNodes(String xmlFileName, String xpathExpression) { + NodeList nodeList = null; + try { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + + // === XXE Protection === + dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); + dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + dbf.setXIncludeAware(false); + dbf.setExpandEntityReferences(false); + + DocumentBuilder db = dbf.newDocumentBuilder(); + File file = new File(xmlFileName); + Document document = db.parse(file); + + XPathExpression xPathExpr = XPathFactory.newInstance().newXPath().compile(xpathExpression); + nodeList = (NodeList) xPathExpr.evaluate(document, XPathConstants.NODESET); + + } catch (Exception ex) { + LoggerFactory.getLogger(ApiResource.class.getName()).error("Error parsing XML file: " + xmlFileName, ex); + // return null on error + } + return nodeList; + } + private CommandResult importCertificateByStream(String itemId, MultivaluedMap formParams) throws Exception { try { List params = new ArrayList(); @@ -245,7 +358,7 @@ private CommandResult importCertificateByStream(String itemId, MultivaluedMap