From 53d83244c1bf36af86e90ce5fe758a369f73563e Mon Sep 17 00:00:00 2001 From: "serena.spinoso" Date: Sat, 25 Feb 2017 12:00:55 +0100 Subject: Add verigraph code base JIRA: PARSER-111 Change-Id: Ie76e14fabbb6c388ebc89d9a15dd3021b25fa892 Signed-off-by: serena.spinoso --- .../escape/verify/client/Neo4jManagerClient.java | 339 ++++++ .../polito/escape/verify/client/VerifyClient.java | 444 ++++++++ .../verify/client/VerifyClientException.java | 24 + .../escape/verify/database/DatabaseClass.java | 175 +++ .../ConfigurationCustomDeserializer.java | 42 + .../deserializer/GraphCustomDeserializer.java | 87 ++ .../deserializer/NodeCustomDeserializer.java | 92 ++ .../deserializer/PathsMessageBodyReader.java | 52 + .../verify/exception/BadRequestException.java | 23 + .../exception/BadRequestExceptionMapper.java | 30 + .../verify/exception/DataNotFoundException.java | 23 + .../exception/DataNotFoundExceptionMapper.java | 30 + .../verify/exception/ForbiddenException.java | 23 + .../verify/exception/ForbiddenExceptionMapper.java | 30 + .../verify/exception/GenericExceptionMapper.java | 29 + .../exception/InternalServerErrorException.java | 23 + .../InternalServerErrorExceptionMapper.java | 30 + .../polito/escape/verify/model/Configuration.java | 75 ++ .../java/it/polito/escape/verify/model/Entry.java | 37 + .../polito/escape/verify/model/ErrorMessage.java | 62 + .../java/it/polito/escape/verify/model/Graph.java | 105 ++ .../java/it/polito/escape/verify/model/Link.java | 71 ++ .../it/polito/escape/verify/model/Neighbour.java | 70 ++ .../java/it/polito/escape/verify/model/Node.java | 152 +++ .../java/it/polito/escape/verify/model/Test.java | 62 + .../polito/escape/verify/model/Verification.java | 69 ++ .../it/polito/escape/verify/model/jaxb.properties | 1 + .../escape/verify/resources/GraphResource.java | 167 +++ .../escape/verify/resources/NeighbourResource.java | 141 +++ .../escape/verify/resources/NodeResource.java | 230 ++++ .../verify/resources/beans/VerificationBean.java | 67 ++ .../serializer/CustomConfigurationSerializer.java | 35 + .../verify/serializer/CustomMapSerializer.java | 30 + .../polito/escape/verify/service/GraphService.java | 129 +++ .../verify/service/JsonValidationService.java | 136 +++ .../escape/verify/service/NeighbourService.java | 182 +++ .../polito/escape/verify/service/NodeService.java | 258 +++++ .../escape/verify/service/ValidationUtils.java | 132 +++ .../escape/verify/service/VerificationService.java | 1185 ++++++++++++++++++++ .../escape/verify/test/MultiThreadedTestCase.java | 201 ++++ .../it/polito/escape/verify/test/Scalability.java | 409 +++++++ .../it/polito/escape/verify/test/TestCase.java | 161 +++ .../escape/verify/test/TestExecutionException.java | 24 + .../java/it/polito/escape/verify/test/Tester.java | 220 ++++ .../escape/verify/validation/DpiValidator.java | 45 + .../escape/verify/validation/EndhostValidator.java | 25 + .../verify/validation/ValidationInterface.java | 21 + .../verify/validation/VpnaccessValidator.java | 51 + .../escape/verify/validation/VpnexitValidator.java | 51 + .../validation/exception/ValidationException.java | 31 + 50 files changed, 6131 insertions(+) create mode 100644 verigraph/src/main/java/it/polito/escape/verify/client/Neo4jManagerClient.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/client/VerifyClient.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/client/VerifyClientException.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/database/DatabaseClass.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/deserializer/ConfigurationCustomDeserializer.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/deserializer/GraphCustomDeserializer.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/deserializer/NodeCustomDeserializer.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/deserializer/PathsMessageBodyReader.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/exception/BadRequestException.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/exception/BadRequestExceptionMapper.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/exception/DataNotFoundException.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/exception/DataNotFoundExceptionMapper.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/exception/ForbiddenException.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/exception/ForbiddenExceptionMapper.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/exception/GenericExceptionMapper.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/exception/InternalServerErrorException.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/exception/InternalServerErrorExceptionMapper.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/model/Configuration.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/model/Entry.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/model/ErrorMessage.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/model/Graph.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/model/Link.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/model/Neighbour.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/model/Node.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/model/Test.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/model/Verification.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/model/jaxb.properties create mode 100644 verigraph/src/main/java/it/polito/escape/verify/resources/GraphResource.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/resources/NeighbourResource.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/resources/NodeResource.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/resources/beans/VerificationBean.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/serializer/CustomConfigurationSerializer.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/serializer/CustomMapSerializer.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/service/GraphService.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/service/JsonValidationService.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/service/NeighbourService.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/service/NodeService.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/service/ValidationUtils.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/service/VerificationService.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/test/MultiThreadedTestCase.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/test/Scalability.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/test/TestCase.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/test/TestExecutionException.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/test/Tester.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/validation/DpiValidator.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/validation/EndhostValidator.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/validation/ValidationInterface.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/validation/VpnaccessValidator.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/validation/VpnexitValidator.java create mode 100644 verigraph/src/main/java/it/polito/escape/verify/validation/exception/ValidationException.java (limited to 'verigraph/src/main/java/it/polito/escape/verify') diff --git a/verigraph/src/main/java/it/polito/escape/verify/client/Neo4jManagerClient.java b/verigraph/src/main/java/it/polito/escape/verify/client/Neo4jManagerClient.java new file mode 100644 index 0000000..044c136 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/client/Neo4jManagerClient.java @@ -0,0 +1,339 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.client; + +import java.io.StringWriter; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.stream.FactoryConfigurationError; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +import it.polito.escape.verify.deserializer.PathsMessageBodyReader; +import it.polito.escape.verify.model.Entry; +import it.polito.nffg.neo4j.jaxb.ActionEnumType; +import it.polito.nffg.neo4j.jaxb.ActionType; +import it.polito.nffg.neo4j.jaxb.ActionsType; +import it.polito.nffg.neo4j.jaxb.CiType; +import it.polito.nffg.neo4j.jaxb.CiType.Attributes; +import it.polito.nffg.neo4j.jaxb.CiType.Attributes.Attribute; +import it.polito.nffg.neo4j.jaxb.CpType; +import it.polito.nffg.neo4j.jaxb.CpointsType; +import it.polito.nffg.neo4j.jaxb.CtrlInterfacesType; +import it.polito.nffg.neo4j.jaxb.EpCpType; +import it.polito.nffg.neo4j.jaxb.EpType; +import it.polito.nffg.neo4j.jaxb.EpType.Flowspace; +import it.polito.nffg.neo4j.jaxb.EpointsType; +import it.polito.nffg.neo4j.jaxb.EpsCpsType; +import it.polito.nffg.neo4j.jaxb.FlowrulesType; +import it.polito.nffg.neo4j.jaxb.FlowrulesType.Flowspace.Tcp; +import it.polito.nffg.neo4j.jaxb.MonParamsType; +import it.polito.nffg.neo4j.jaxb.MonParamsType.Parameter; +import it.polito.nffg.neo4j.jaxb.NeType; +import it.polito.nffg.neo4j.jaxb.NelementsType; +import it.polito.nffg.neo4j.jaxb.NfType; +import it.polito.nffg.neo4j.jaxb.Nffg; +import it.polito.nffg.neo4j.jaxb.NfunctionsType; +import it.polito.nffg.neo4j.jaxb.ObjectFactory; +import it.polito.nffg.neo4j.jaxb.Paths; +import it.polito.nffg.neo4j.jaxb.PortDirEnumType; +import it.polito.nffg.neo4j.jaxb.PortType; +import it.polito.nffg.neo4j.jaxb.SpecType; +import it.polito.nffg.neo4j.jaxb.SpecType.Cpu; +import it.polito.nffg.neo4j.jaxb.SpecType.Deployment; +import it.polito.nffg.neo4j.jaxb.SpecType.Image; +import it.polito.nffg.neo4j.jaxb.SpecType.Memory; +import it.polito.nffg.neo4j.jaxb.SpecType.Storage; + +public class Neo4jManagerClient { + + private JAXBContext jc; + + private String address; + + private Nffg nffg; + + private List endpoints = new LinkedList(); + + private List firewalls = new LinkedList(); + + private Map> routingTable = new HashMap>(); + + private String source; + + private String destination; + + private String xmlString; + + private WebTarget baseTarget; + + public Neo4jManagerClient() { + + } + + public Neo4jManagerClient( String address, String source, String destination, List endpoints, + List firewalls, Map> routingTable) { + this.address = address; + this.source = source; + this.destination = destination; + this.endpoints = endpoints; + this.firewalls = firewalls; + this.routingTable = routingTable; + + Client client = ClientBuilder.newBuilder().register(PathsMessageBodyReader.class).build(); + + this.baseTarget = client.target(this.address); + } + + public Paths getPaths() throws Exception { + try { + this.generateCustomXml(); + } + catch (JAXBException e) { + throw (e); + } + + WebTarget graphsTarget = baseTarget.path("graphs"); + WebTarget pathSourceDestination = graphsTarget.path("{graphId}/paths"); + WebTarget deleteNffg = graphsTarget.path("{graphId}"); + + Response deleteNffgResponse = deleteNffg.resolveTemplate("graphId", "1").request().delete(); + if (deleteNffgResponse.getStatus() != javax.ws.rs.core.Response.Status.NO_CONTENT.getStatusCode() + && deleteNffgResponse.getStatus() != javax.ws.rs.core.Response.Status.NOT_FOUND.getStatusCode()) { + throw new Exception("graph deletion failed"); + } + + Response createNffgResponse = graphsTarget .request("application/xml") + .post(Entity.entity(this.xmlString, "application/xml")); + if (createNffgResponse.getStatus() != javax.ws.rs.core.Response.Status.CREATED.getStatusCode()) { + throw new Exception("graph creation failed"); + } + + System.out.println("Getting paths from node \"" + this.source + "\" to node \"" + this.destination + "\"..."); + Response getPath = pathSourceDestination.resolveTemplate("graphId", "1") + .queryParam("src", this.source) + .queryParam("dst", this.destination) + .queryParam("dir", "outgoing") + .request(MediaType.APPLICATION_XML) + .get(); + + System.out.println("Paths from node \"" + this.source + "\" to node \"" + this.destination + "\":"); + + Paths paths = null; + try { + paths = getPath.readEntity(Paths.class); + } + catch (ProcessingException e) { + throw (e); + } + catch (IllegalStateException e) { + throw (e); + } + + return paths; + } + + private void generateCustomXml() throws JAXBException { + + jc = JAXBContext.newInstance("it.polito.nffg.neo4j.jaxb"); + + nffg = new Nffg(); + nffg.setId("nffg_1"); + + generateEndpoints(); + generateFirewalls(); + generateConnections(); + + MonParamsType monitoring_parameters = new MonParamsType(); + nffg.setMonitoringParameters(monitoring_parameters); + + JAXBElement root = (new ObjectFactory()).createNffg(nffg); + + Marshaller m; + try { + m = jc.createMarshaller(); + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + + StringWriter stringWriter = new StringWriter(); + try { + XMLStreamWriter xmlStreamWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(stringWriter); + m.marshal(root, xmlStreamWriter); + xmlString = stringWriter.getBuffer().toString(); + } + catch (XMLStreamException e) { + e.printStackTrace(); + } + catch (FactoryConfigurationError e) { + + e.printStackTrace(); + } + // m.marshal( root, new File("nffg.xml") ); + System.out.println(xmlString); + + } + catch (JAXBException e) { + throw (e); + } + } + + private void generateConnections() { + NelementsType network_elements = new NelementsType(); + NeType network_element = new NeType(); + network_element.setId("ne_1"); + network_element.setType("BiSBiS"); + EpsCpsType ep_cps = new EpsCpsType(); + + for (String node : routingTable.keySet()) { + EpCpType ep_cp = new EpCpType(); + ep_cp.setIdRef(node); + FlowrulesType flowrules = new FlowrulesType(); + it.polito.nffg.neo4j.jaxb.FlowrulesType.Flowspace flowspace = + new it.polito.nffg.neo4j.jaxb.FlowrulesType.Flowspace(); + flowrules.setFlowspace(flowspace); + ActionsType actions = new ActionsType(); + for (Entry e : routingTable.get(node)) { + ActionType action = new ActionType(); + action.setType(ActionEnumType.fromValue(e.getDirection())); + action.setPort(e.getDestination()); + actions.getAction().add(action); + } + flowrules.setActions(actions); + + ep_cp.getFlowrules().add(flowrules); + ep_cps.getEpCp().add(ep_cp); + } + network_element.setEpsCps(ep_cps); + + MonParamsType monitoring_parameters = new MonParamsType(); + Parameter parameter = new Parameter(); + parameter.getValue().add("Bandwith ep_1 cp_1 100mbit"); + monitoring_parameters.getParameter().add(parameter); + Parameter parameter2 = new Parameter(); + parameter2.getValue().add("Delay ep_1 cp_1 50ms"); + monitoring_parameters.getParameter().add(parameter2); + + network_element.setMonitoringParameters(monitoring_parameters); + + network_elements.getNetworkElement().add(network_element); + + nffg.setNetworkElements(network_elements); + + } + + private void generateFirewalls() { + NfunctionsType network_functions = new NfunctionsType(); + + for (String firewall : firewalls) { + NfType nf = new NfType(); + nf.setId(firewall); + nf.setFunctionalType("firewall"); + + SpecType specification = new SpecType(); + Deployment deployment = new Deployment(); + deployment.setType("PolitoFirewall"); + Image image = new Image(); + image.setUri("http://www.polito.it"); + Cpu cpu = new Cpu(); + cpu.setNumCores((short) (7)); + Memory memory = new Memory(); + memory.setSize("10MiB"); + Storage storage = new Storage(); + storage.setSize("100MiB"); + specification.setDeployment(deployment); + specification.setImage(image); + specification.setCpu(cpu); + specification.setMemory(memory); + specification.setStorage(storage); + + CpointsType connection_points = new CpointsType(); + CpType connection_point = new CpType(); + connection_point.setId(firewall + "_in"); + PortType port = new PortType(); + port.setId(79); + port.setDirection(PortDirEnumType.IN); + port.setType("GbE"); + connection_point.setPort(port); + connection_points.getConnectionPoint().add(connection_point); + + CpType connection_point2 = new CpType(); + connection_point2.setId(firewall + "_out"); + PortType port2 = new PortType(); + port2.setId(77); + port2.setDirection(PortDirEnumType.OUT); + port2.setType("10GbE"); + connection_point2.setPort(port2); + + connection_points.getConnectionPoint().add(connection_point2); + + CtrlInterfacesType control_interfaces = new CtrlInterfacesType(); + CiType control_interface = new CiType(); + control_interface.setId(firewall + "_ci"); + + Attributes attributes = new Attributes(); + Attribute attribute = new Attribute(); + attribute.setValue("tcp://127.0.0.1:5555"); + attributes.getAttribute().add(attribute); + Attribute attribute2 = new Attribute(); + attribute2.setValue("Netconf"); + attributes.getAttribute().add(attribute2); + control_interface.setAttributes(attributes); + + control_interfaces.getControlInterface().add(control_interface); + + MonParamsType monitoring_parameters = new MonParamsType(); + Parameter parameter = new Parameter(); + parameter.getValue().add("Measure script"); + monitoring_parameters.getParameter().add(parameter); + + nf.setSpecification(specification); + nf.setConnectionPoints(connection_points); + nf.setControlInterfaces(control_interfaces); + nf.setMonitoringParameters(monitoring_parameters); + + network_functions.getNetworkFunction().add(nf); + + } + nffg.setNetworkFunctions(network_functions); + + } + + private void generateEndpoints() { + EpointsType eps = new EpointsType(); + + for (String e : endpoints) { + EpType endpoint = new EpType(); + endpoint.setId(e); + Flowspace flowspace = new Flowspace(); + flowspace.setIngPhysPort("10"); + Tcp tcp = new Tcp(); + tcp.setSrc(80); + flowspace.setTcp(tcp); + endpoint.setFlowspace(flowspace); + eps.getEndpoint().add(endpoint); + } + nffg.setEndpoints(eps); + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/client/VerifyClient.java b/verigraph/src/main/java/it/polito/escape/verify/client/VerifyClient.java new file mode 100644 index 0000000..1997553 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/client/VerifyClient.java @@ -0,0 +1,444 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.client; + +import java.io.File; +import java.io.FileReader; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.ProcessingException; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.ResponseProcessingException; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import it.polito.escape.verify.model.ErrorMessage; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Neighbour; +import it.polito.escape.verify.model.Node; +import it.polito.escape.verify.model.Verification; + +public class VerifyClient { + + private WebTarget baseTarget; + + private WebTarget graphsTarget; + + private WebTarget graphTarget; + + private WebTarget nodesTarget; + + private WebTarget nodeTarget; + + private WebTarget neighboursTarget; + + private WebTarget neighbourTarget; + + private WebTarget reachabilityTarget; + + private WebTarget isolationTarget; + + private WebTarget traversalTarget; + + public VerifyClient(String address) { + Client client = ClientBuilder.newClient(); + + this.baseTarget = client.target(address); + this.graphsTarget = baseTarget.path("graphs"); + this.graphTarget = graphsTarget.path("/{graphId}"); + this.nodesTarget = graphTarget.path("/nodes"); + this.nodeTarget = nodesTarget.path("//{nodeId}"); + this.neighboursTarget = nodeTarget.path("/neighbours"); + this.neighbourTarget = neighboursTarget.path("/{neighbourId}"); + this.reachabilityTarget = graphTarget.path("/policy"); + this.isolationTarget = graphTarget.path("/policy"); + this.traversalTarget = graphTarget.path("/policy"); + } + + public void checkResponse(Response response) throws VerifyClientException { + int status = response.getStatus(); + + // 400 + if (status == Response.Status.BAD_REQUEST.getStatusCode()) { + try { + // String responseString = response.readEntity(String.class); + // System.out.println(responseString); + ErrorMessage errorMessage = response.readEntity(ErrorMessage.class); + String message = errorMessage.getErrorMessage(); + throw new VerifyClientException("Bad request: " + message); + } + catch (ProcessingException e) { + throw new VerifyClientException("the content of the message cannot be mapped to an entity of the 'ErrorMessage': " + + e.getMessage()); + } + catch (IllegalStateException e) { + throw new VerifyClientException("the entity is not backed by an input stream or the original entity input stream has already been consumed without buffering the entity data prior consuming: " + + e.getMessage()); + } + } + // 403 + if (status == Response.Status.FORBIDDEN.getStatusCode()) { + try { + ErrorMessage errorMessage = response.readEntity(ErrorMessage.class); + String message = errorMessage.getErrorMessage(); + throw new VerifyClientException("Forbidden: " + message); + } + catch (ProcessingException e) { + throw new VerifyClientException("the content of the message cannot be mapped to an entity of the 'ErrorMessage': " + + e.getMessage()); + } + catch (IllegalStateException e) { + throw new VerifyClientException("the entity is not backed by an input stream or the original entity input stream has already been consumed without buffering the entity data prior consuming: " + + e.getMessage()); + } + } + // 404 + if (status == Response.Status.NOT_FOUND.getStatusCode()) { + try { + ErrorMessage errorMessage = response.readEntity(ErrorMessage.class); + String message = errorMessage.getErrorMessage(); + throw new VerifyClientException("Not found: " + message); + } + catch (ProcessingException e) { + throw new VerifyClientException("the content of the message cannot be mapped to an entity of the 'ErrorMessage': " + + e.getMessage()); + } + catch (IllegalStateException e) { + throw new VerifyClientException("the 'Response' entity is not backed by an input stream or the original entity input stream has already been consumed without buffering the entity data prior consuming: " + + e.getMessage()); + } + } + // 500 + if (status == Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()) { + try { + ErrorMessage errorMessage = response.readEntity(ErrorMessage.class); + String message = errorMessage.getErrorMessage(); + throw new VerifyClientException("Internal server error: " + message); + } + catch (ProcessingException e) { + throw new VerifyClientException("the content of the message cannot be mapped to an entity of the 'ErrorMessage': " + + e.getMessage()); + } + catch (IllegalStateException e) { + throw new VerifyClientException("the entity is not backed by an input stream or the original entity input stream has already been consumed without buffering the entity data prior consuming: " + + e.getMessage()); + } + } + if (status != Response.Status.ACCEPTED.getStatusCode() && status != Response.Status.CREATED.getStatusCode() + && status != Response.Status.NO_CONTENT.getStatusCode() && status != Response.Status.OK.getStatusCode()) + throw new VerifyClientException("Unknown error"); + } + + public Response createGraph(Graph graph) throws VerifyClientException, ResponseProcessingException, ProcessingException { + Response response = graphsTarget.request().post(Entity.json(graph)); + checkResponse(response); + return response; + } + + public Response createGraph(String graph) throws VerifyClientException, ResponseProcessingException, ProcessingException { + Response response = graphsTarget.request().post(Entity.entity(graph, "application/json")); + checkResponse(response); + return response; + } + + public Response retrieveGraph(long graphId) throws VerifyClientException, ProcessingException { + Response response = graphTarget.resolveTemplate("graphId", graphId).request().get(); + checkResponse(response); + return response; + } + + public Response updateGraph(long graphId, Graph graph) throws VerifyClientException, ResponseProcessingException, ProcessingException { + Response response = graphTarget.resolveTemplate("graphId", graphId).request().put(Entity.json(graph)); + checkResponse(response); + return response; + } + + public Response deleteGraph(long graphId) throws VerifyClientException, ResponseProcessingException, ProcessingException { + Response response = graphTarget.resolveTemplate("graphId", graphId).request().delete(); + checkResponse(response); + return response; + } + + public Response createNode(long graphId, Node node) throws VerifyClientException, ResponseProcessingException, ProcessingException { + Response response = nodesTarget.resolveTemplate("graphId", graphId).request().post(Entity.json(node)); + checkResponse(response); + return response; + } + + public Response retrieveNode(long graphId, long nodeId) throws VerifyClientException, ProcessingException { + Response response = nodeTarget .resolveTemplate("graphId", graphId) + .resolveTemplate("nodeId", nodeId) + .request() + .get(); + checkResponse(response); + return response; + } + + public Response updateNode(long graphId, long nodeId, Node node) throws VerifyClientException, ResponseProcessingException, ProcessingException { + Response response = nodeTarget .resolveTemplate("graphId", graphId) + .resolveTemplate("nodeId", nodeId) + .request() + .put(Entity.json(node)); + checkResponse(response); + return response; + } + + public Response deleteNode(long graphId, long nodeId) throws VerifyClientException, ResponseProcessingException, ProcessingException { + Response response = nodeTarget .resolveTemplate("graphId", graphId) + .resolveTemplate("nodeId", nodeId) + .request() + .delete(); + checkResponse(response); + return response; + } + + public Response createNeighbour(long graphId, long nodeId, Neighbour neighbour) throws VerifyClientException, ResponseProcessingException, ProcessingException { + Response response = neighboursTarget.resolveTemplate("graphId", graphId) + .resolveTemplate("nodeId", nodeId) + .request() + .post(Entity.json(neighbour)); + checkResponse(response); + return response; + } + + public Response retrieveNeighbour(long graphId, long nodeId, long neighbourId) throws VerifyClientException, ProcessingException { + Response response = neighbourTarget .resolveTemplate("graphId", graphId) + .resolveTemplate("nodeId", nodeId) + .resolveTemplate("neighbourId", neighbourId) + .request() + .get(); + checkResponse(response); + return response; + } + + public Response updateNeighbour(long graphId, long nodeId, long neighbourId, + Neighbour neighbour) throws VerifyClientException, ResponseProcessingException, ProcessingException { + Response response = neighbourTarget .resolveTemplate("graphId", graphId) + .resolveTemplate("nodeId", nodeId) + .resolveTemplate("neighbourId", neighbourId) + .request() + .put(Entity.json(neighbour)); + checkResponse(response); + return response; + } + + public Response deleteNeighbour(long graphId, long nodeId, long neighbourId) throws VerifyClientException, ResponseProcessingException, ProcessingException { + Response response = neighbourTarget .resolveTemplate("graphId", graphId) + .resolveTemplate("nodeId", nodeId) + .resolveTemplate("neighbourId", neighbourId) + .request() + .delete(); + checkResponse(response); + return response; + } + + public Verification getReachability(long graphId, String source, String destination) throws VerifyClientException, ProcessingException{ + Response response = reachabilityTarget.resolveTemplate("graphId", graphId) + .queryParam("source", source) + .queryParam("destination", destination) + .queryParam("type", "reachability") + .request() + .get(); + checkResponse(response); + try{ + Verification verification = response.readEntity(Verification.class); + return verification; + } + catch (ProcessingException e) { + throw new VerifyClientException("the content of the message cannot be mapped to an entity of the 'Verification': " + + e.getMessage()); + } + catch (IllegalStateException e) { + throw new VerifyClientException("the 'Verification' entity is not backed by an input stream or the original entity input stream has already been consumed without buffering the entity data prior consuming: " + + e.getMessage()); + } + } + + public Verification getIsolation(long graphId, String source, String destination, String middlebox) throws VerifyClientException, ProcessingException{ + Response response = isolationTarget.resolveTemplate("graphId", graphId) + .queryParam("source", source) + .queryParam("destination", destination) + .queryParam("middlebox", middlebox) + .queryParam("type", "isolation") + .request() + .get(); + checkResponse(response); + try{ + Verification verification = response.readEntity(Verification.class); + return verification; + } + catch (ProcessingException e) { + throw new VerifyClientException("the content of the message cannot be mapped to an entity of the 'Verification': " + + e.getMessage()); + } + catch (IllegalStateException e) { + throw new VerifyClientException("the 'Verification' entity is not backed by an input stream or the original entity input stream has already been consumed without buffering the entity data prior consuming: " + + e.getMessage()); + } + } + + public Verification getTraversal(long graphId, String source, String destination, String middlebox) throws VerifyClientException, ProcessingException{ + Response response = traversalTarget.resolveTemplate("graphId", graphId) + .queryParam("source", source) + .queryParam("destination", destination) + .queryParam("middlebox", middlebox) + .queryParam("type", "traversal") + .request() + .get(); + checkResponse(response); + try{ + Verification verification = response.readEntity(Verification.class); + return verification; + } + catch (ProcessingException e) { + throw new VerifyClientException("the content of the message cannot be mapped to an entity of the 'Verification': " + + e.getMessage()); + } + catch (IllegalStateException e) { + throw new VerifyClientException("the 'Verification' entity is not backed by an input stream or the original entity input stream has already been consumed without buffering the entity data prior consuming: " + + e.getMessage()); + } + } + + @SuppressWarnings("unused") + private static String deserializeString(File file) throws IOException { + int len; + char[] chr = new char[4096]; + final StringBuffer buffer = new StringBuffer(); + final FileReader reader = new FileReader(file); + try { + while ((len = reader.read(chr)) > 0) { + buffer.append(chr, 0, len); + } + } + finally { + reader.close(); + } + return buffer.toString(); + } + + public List getFiles() { + List filesList = new ArrayList(); + + String folderString = System.getProperty("folder"); + File folder; + if (folderString == null) + folder = new File(System.getProperty("user.dir") + "/examples"); + else + folder = new File(folderString); + + System.out.println("Folder set to " + folder.getAbsolutePath()); + + File[] files = folder.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".json"); + } + }); + + for (File f : files) { + filesList.add(f); + } + + return filesList; + } + + public Graph addGraphFromFile(File file) throws JsonParseException, JsonMappingException, IOException, Exception { + System.out.println("Parsing graph of file '" + file.getAbsolutePath() + "'..."); + Graph graph = new ObjectMapper().readValue(file, Graph.class); + Response createGraphResponse = createGraph(graph); + if (createGraphResponse.getStatus() != Status.CREATED.getStatusCode()) { + throw new Exception("Creation of graph contained in file '" + file.getAbsolutePath() + "' returned status " + + createGraphResponse.getStatus()); + } + String responseString = createGraphResponse.readEntity(String.class); + System.out.println("Response:"); + System.out.println(responseString); + Graph response = new ObjectMapper().readValue(responseString, Graph.class); + printGraph(response); + return response; + } + + public void printGraph(Graph graph) { + System.out.println("Graph " + graph.getId()); + for (Node n : graph.getNodes().values()) { + System.out.println("\tNode " + n.getId()); + System.out.println("\tName " + n.getName()); + System.out.println("\tFunctional type: " + n.getFunctional_type()); + for (Neighbour neighbour : n.getNeighbours().values()) { + System.out.println("\t\tNeighbour " + neighbour.getId()); + System.out.println("\t\tName: " + neighbour.getName()); + } + } + } + + public Map addGraphsFromFiles(List files) throws JsonParseException, JsonMappingException, IOException, + Exception { + Map graphs = new HashMap(); + + for (File f : files) { + Graph graph = addGraphFromFile(f); + graphs.put(f.getName(), graph); + } + + for (Map.Entry graph : graphs.entrySet()) { + System.out.println(graph.getKey() + " -> graph " + graph.getValue().getId()); + } + System.out.println("Graphs added"); + + return graphs; + } + + public static void main(String[] args) throws IOException, Exception { + System.out.println("Adding graphs"); + + VerifyClient verifyClient = new VerifyClient("http://localhost:8080/verify/api"); + + List files = verifyClient.getFiles(); + Map graphs = verifyClient.addGraphsFromFiles(files); + + for (Graph g : graphs.values()) { + Response response = verifyClient.retrieveGraph(g.getId()); + String responseString = response.readEntity(String.class); + + System.out.println("Response"); + System.out.println(responseString); + Graph graph = new ObjectMapper().readValue(responseString, Graph.class); + System.out.println("Read graph " + graph.getId()); + System.out.println(response.getStatus()); + } + + Graph graph = graphs.get("budapest_sat.json"); + System.out.println("graphId set to " + graph.getId()); + System.out.println("Getting reachability from 'user1' to 'websever' in 'budapest' graph (expecting SAT)..."); + Verification verification = verifyClient.getReachability(graph.getId(), "user1", "webserver"); + System.out.println(verification.getResult()); + graph = graphs.get("budapest_unsat.json"); + System.out.println("graphId set to " + graph.getId()); + System.out.println("Getting reachability from 'user1' to 'websever' in 'budapest' graph (expecting UNSAT)..."); + verification = verifyClient.getReachability(graph.getId(), "user1", "webserver"); + System.out.println(verification.getResult()); + + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/client/VerifyClientException.java b/verigraph/src/main/java/it/polito/escape/verify/client/VerifyClientException.java new file mode 100644 index 0000000..d91d31e --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/client/VerifyClientException.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.client; + + +public class VerifyClientException extends Exception{ + + /** + * + */ + private static final long serialVersionUID = 4794956747093682854L; + + public VerifyClientException(String message){ + super(message); + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/database/DatabaseClass.java b/verigraph/src/main/java/it/polito/escape/verify/database/DatabaseClass.java new file mode 100644 index 0000000..7837e51 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/database/DatabaseClass.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.database; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import it.polito.escape.verify.exception.InternalServerErrorException; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Neighbour; +import it.polito.escape.verify.model.Node; +import it.polito.escape.verify.service.GraphService; + +public class DatabaseClass { + + private static final DatabaseClass instance = new DatabaseClass(); + + private static ConcurrentHashMap graphs; + + private static String persistenceFile; + + private static boolean enablePersistence; + + protected DatabaseClass() { + initialize(); + if (enablePersistence) + loadDatabase(); + } + + private void initialize() { + graphs = new ConcurrentHashMap<>(); + enablePersistence = false; + persistenceFile = System.getProperty("catalina.base") + "/webapps/verify/json/" + "database.json"; + } + + private void loadDatabase() { + ObjectMapper mapper = new ObjectMapper(); + + List parsedGraphs = null; + try { + File databaseFile = new File(persistenceFile); + parsedGraphs = mapper.readValue(databaseFile, + TypeFactory.defaultInstance().constructCollectionType( List.class, + Graph.class)); + } + catch (JsonParseException e) { + System.out.println("Database not loaded due to a JsonParseException: " + e.getMessage()); + return; + } + catch (JsonMappingException e) { + System.out.println("Database not loaded due to a JsonMappingException: " + e.getMessage()); + return; + } + catch (IOException e) { + //retry changing path + persistenceFile = "src/main/webapp/json/" + "database.json"; + + try { + File databaseFile = new File(persistenceFile); + parsedGraphs = mapper.readValue(databaseFile, + TypeFactory.defaultInstance().constructCollectionType( List.class, + Graph.class)); + } catch (JsonParseException e1) { + System.out.println("Database not loaded due to a JsonParseException: " + e.getMessage()); + return; + } catch (JsonMappingException e1) { + System.out.println("Database not loaded due to a JsonMappingException: " + e.getMessage()); + return; + } catch (IOException e1) { + System.out.println("Database not loaded due to an IOException: " + e.getMessage()); + return; + } + } + + System.out.println("Loading database..."); + + for (Graph graph : parsedGraphs) { + + try { + GraphService.validateGraph(graph); + } + catch (Exception e) { + System.out.println("Invalid database file: at least one graph is invalid!"); + return; + } + + graph.setId(getNumberOfGraphs() + 1); + + for (Map.Entry nodeEntry : graph.getNodes().entrySet()) { + nodeEntry.getValue().setId(nodeEntry.getKey()); + + for (Map.Entry neighbourEntry : nodeEntry.getValue().getNeighbours().entrySet()) { + neighbourEntry.getValue().setId(neighbourEntry.getKey()); + } + } + + graphs.put(graph.getId(), graph); + } + + System.out.println("Database loaded!"); + System.out.println(graphs.size() + " graphs added"); + } + + public static DatabaseClass getInstance() { + return instance; + } + + public ConcurrentHashMap getGraphs() { + return graphs; + } + + public synchronized int getNumberOfGraphs() { + return graphs.size(); + } + + public synchronized int getGraphNumberOfNodes(long graphId) { + Graph graph = graphs.get(graphId); + if (graph == null) + return 0; + Map nodes = graph.getNodes(); + if (nodes == null) + return 0; + return nodes.size(); + } + + public static void persistDatabase() { + if (!enablePersistence) + return; + ObjectMapper mapper = new ObjectMapper(); + + try { + mapper.writeValue(new File(persistenceFile), graphs); + } + catch (JsonGenerationException e) { + throw new InternalServerErrorException("Unable to persist database due to a JsonGenerationException: " + + e.getMessage()); + } + catch (JsonMappingException e) { + throw new InternalServerErrorException("Unable to persist database due to a JsonMappingException: " + + e.getMessage()); + } + catch (IOException e) { + //retry changing path + persistenceFile = "src/main/webapp/json/" + "database.json"; + try { + mapper.writeValue(new File(persistenceFile), graphs); + } catch (JsonGenerationException e1) { + throw new InternalServerErrorException("Unable to persist database due to a JsonGenerationException: " + + e.getMessage()); + } catch (JsonMappingException e1) { + throw new InternalServerErrorException("Unable to persist database due to a JsonGenerationException: " + + e.getMessage()); + } catch (IOException e1) { + throw new InternalServerErrorException("Unable to persist database due to an IOException: " + + e.getMessage()); + } + } + } +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/deserializer/ConfigurationCustomDeserializer.java b/verigraph/src/main/java/it/polito/escape/verify/deserializer/ConfigurationCustomDeserializer.java new file mode 100644 index 0000000..5cdd084 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/deserializer/ConfigurationCustomDeserializer.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.deserializer; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; + +import it.polito.escape.verify.exception.InternalServerErrorException; +import it.polito.escape.verify.model.Configuration; + +public class ConfigurationCustomDeserializer extends JsonDeserializer { + + @Override + public Configuration deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, + JsonProcessingException { + try { + JsonNode root = jp.getCodec().readTree(jp); + + return new Configuration("", "", root); + } + catch (JsonProcessingException e) { + throw new InternalServerErrorException("Error parsing configuration: " + e.getMessage()); + } + catch (IOException e) { + throw new InternalServerErrorException("I/O error parsing configuration: " + e.getMessage()); + } + + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/deserializer/GraphCustomDeserializer.java b/verigraph/src/main/java/it/polito/escape/verify/deserializer/GraphCustomDeserializer.java new file mode 100644 index 0000000..f71c996 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/deserializer/GraphCustomDeserializer.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.deserializer; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import it.polito.escape.verify.exception.BadRequestException; +import it.polito.escape.verify.exception.InternalServerErrorException; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Node; + + +/** + * The Class GraphCustomDeserializer is a custom deserializer for a Graph object + */ +public class GraphCustomDeserializer extends JsonDeserializer{ + + /* (non-Javadoc) + * @see com.fasterxml.jackson.databind.JsonDeserializer#deserialize(com.fasterxml.jackson.core.JsonParser, com.fasterxml.jackson.databind.DeserializationContext) + */ + @Override + public Graph deserialize(JsonParser jp, DeserializationContext context){ + JsonNode root = null; + try { + root = jp.getCodec().readTree(jp); + } + catch (JsonProcessingException e) { + throw new InternalServerErrorException("Error parsing a graph: " + e.getMessage()); + } + catch (IOException e) { + throw new InternalServerErrorException("I/O error parsing a graph: " + e.getMessage()); + } + + JsonNode nodesJson = root.get("nodes"); + + if(nodesJson == null) + throw new BadRequestException("Invalid graph"); + + List nodeList = null; + try { + nodeList = new ObjectMapper().readValue(nodesJson.toString(), TypeFactory.defaultInstance().constructCollectionType(List.class, Node.class)); + } + catch (JsonParseException e) { + throw new BadRequestException("Invalid content for a graph: " + e.getMessage()); + } + catch (JsonMappingException e) { + throw new BadRequestException("Invalid input json structure for a graph: " + e.getMessage()); + } + catch (IOException e) { + throw new InternalServerErrorException("I/O error parsing a graph: " + e.getMessage()); + } + + Graph graph = new Graph(); + if(root.get("id") != null){ + long graphId = root.get("id").asLong(); + graph.setId(graphId); + } + Map nodes = graph.getNodes(); + + long numberOfNodes = 0; + for (Node node : nodeList){ + nodes.put(++numberOfNodes, node); + } + return graph; + + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/deserializer/NodeCustomDeserializer.java b/verigraph/src/main/java/it/polito/escape/verify/deserializer/NodeCustomDeserializer.java new file mode 100644 index 0000000..fb451db --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/deserializer/NodeCustomDeserializer.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.deserializer; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import it.polito.escape.verify.exception.BadRequestException; +import it.polito.escape.verify.exception.InternalServerErrorException; +import it.polito.escape.verify.model.Configuration; +import it.polito.escape.verify.model.Neighbour; +import it.polito.escape.verify.model.Node; + +public class NodeCustomDeserializer extends JsonDeserializer { + + @Override + public Node deserialize(JsonParser jp, DeserializationContext context) { + + try { + JsonNode root = jp.getCodec().readTree(jp); + JsonNode neighboursJson = root.get("neighbours"); + JsonNode configurationJson = root.get("configuration"); + + String nodeName = root.get("name").asText(); + String functionalType = root.get("functional_type").asText(); + + Node node = new Node(); + if(root.get("id") != null){ + long nodeId = root.get("id").asLong(); + node.setId(nodeId); + } + node.setName(nodeName); + node.setFunctional_type(functionalType); + + if (configurationJson == null) + node.setConfiguration(new Configuration(node.getName(), "", new ObjectMapper().createArrayNode())); + else { + Configuration conf = node.getConfiguration(); + conf.setId(node.getName()); + conf.setDescription(""); + conf.setConfiguration(configurationJson); + } + + try { + List neighbourList = new ObjectMapper().readValue( neighboursJson.toString(), + TypeFactory .defaultInstance() + .constructCollectionType( List.class, + Neighbour.class)); + Map neighbours = node.getNeighbours(); + + long numberOfNeighbours = 0; + for (Neighbour neighbour : neighbourList) { + neighbours.put(++numberOfNeighbours, neighbour); + } + + return node; + } + catch (JsonParseException e) { + throw new BadRequestException("Invalid content for a node: " + e.getMessage()); + } + catch (JsonMappingException e) { + throw new BadRequestException("Invalid input json structure for a node: " + e.getMessage()); + } + } + catch (JsonProcessingException e) { + throw new InternalServerErrorException("Error parsing a node: " + e.getMessage()); + } + catch (IOException e) { + throw new InternalServerErrorException("I/O error parsing a node: " + e.getMessage()); + } + + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/deserializer/PathsMessageBodyReader.java b/verigraph/src/main/java/it/polito/escape/verify/deserializer/PathsMessageBodyReader.java new file mode 100644 index 0000000..3b54503 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/deserializer/PathsMessageBodyReader.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.deserializer; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import javax.ws.rs.Consumes; +import javax.ws.rs.ProcessingException; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.Provider; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; + +import it.polito.nffg.neo4j.jaxb.Paths; + +@Provider +@Consumes(MediaType.APPLICATION_XML) +public class PathsMessageBodyReader implements MessageBodyReader{ + + @Override + public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return type == Paths.class; + } + + @Override + public Paths readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, InputStream entityStream) + throws IOException, WebApplicationException { + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Paths.class); + Paths paths = (Paths) jaxbContext.createUnmarshaller() + .unmarshal(entityStream); + return paths; + } catch (JAXBException jaxbException) { + throw new ProcessingException("Error deserializing a Paths object.", + jaxbException); + } + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/exception/BadRequestException.java b/verigraph/src/main/java/it/polito/escape/verify/exception/BadRequestException.java new file mode 100644 index 0000000..abbdbf0 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/exception/BadRequestException.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.exception; + +public class BadRequestException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = -548472179073745084L; + + public BadRequestException(String message) { + super(message); + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/exception/BadRequestExceptionMapper.java b/verigraph/src/main/java/it/polito/escape/verify/exception/BadRequestExceptionMapper.java new file mode 100644 index 0000000..8f1ba9c --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/exception/BadRequestExceptionMapper.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.exception; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import it.polito.escape.verify.model.ErrorMessage; + +@Provider +public class BadRequestExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(BadRequestException exception) { + ErrorMessage errorMessage = new ErrorMessage( exception.getMessage(), + 400, + "http://localhost:8080/verify/api-docs/"); + return Response.status(Status.BAD_REQUEST).entity(errorMessage).build(); + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/exception/DataNotFoundException.java b/verigraph/src/main/java/it/polito/escape/verify/exception/DataNotFoundException.java new file mode 100644 index 0000000..5cd1806 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/exception/DataNotFoundException.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.exception; + +public class DataNotFoundException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = -6012364193903183208L; + + public DataNotFoundException(String message) { + super(message); + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/exception/DataNotFoundExceptionMapper.java b/verigraph/src/main/java/it/polito/escape/verify/exception/DataNotFoundExceptionMapper.java new file mode 100644 index 0000000..62e3556 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/exception/DataNotFoundExceptionMapper.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.exception; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import it.polito.escape.verify.model.ErrorMessage; + +@Provider +public class DataNotFoundExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(DataNotFoundException exception) { + ErrorMessage errorMessage = new ErrorMessage( exception.getMessage(), + 404, + "http://localhost:8080/verify/api-docs/"); + return Response.status(Status.NOT_FOUND).entity(errorMessage).build(); + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/exception/ForbiddenException.java b/verigraph/src/main/java/it/polito/escape/verify/exception/ForbiddenException.java new file mode 100644 index 0000000..dc79e97 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/exception/ForbiddenException.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.exception; + +public class ForbiddenException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = -4658914972167044321L; + + public ForbiddenException(String message) { + super(message); + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/exception/ForbiddenExceptionMapper.java b/verigraph/src/main/java/it/polito/escape/verify/exception/ForbiddenExceptionMapper.java new file mode 100644 index 0000000..d060b8b --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/exception/ForbiddenExceptionMapper.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.exception; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import it.polito.escape.verify.model.ErrorMessage; + +@Provider +public class ForbiddenExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(ForbiddenException exception) { + ErrorMessage errorMessage = new ErrorMessage( exception.getMessage(), + 403, + "http://localhost:8080/verify/api-docs/"); + return Response.status(Status.FORBIDDEN).entity(errorMessage).build(); + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/exception/GenericExceptionMapper.java b/verigraph/src/main/java/it/polito/escape/verify/exception/GenericExceptionMapper.java new file mode 100644 index 0000000..96f7caa --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/exception/GenericExceptionMapper.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.exception; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; + +import it.polito.escape.verify.model.ErrorMessage; + +// @Provider +public class GenericExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(Throwable exception) { + ErrorMessage errorMessage = new ErrorMessage("Generic exception: " + exception.getMessage(), + 500, + "http://localhost:8080/verify/api-docs/"); + return Response.status(Status.INTERNAL_SERVER_ERROR).entity(errorMessage).build(); + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/exception/InternalServerErrorException.java b/verigraph/src/main/java/it/polito/escape/verify/exception/InternalServerErrorException.java new file mode 100644 index 0000000..877edb5 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/exception/InternalServerErrorException.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.exception; + +public class InternalServerErrorException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 156815197709461502L; + + public InternalServerErrorException(String message) { + super(message); + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/exception/InternalServerErrorExceptionMapper.java b/verigraph/src/main/java/it/polito/escape/verify/exception/InternalServerErrorExceptionMapper.java new file mode 100644 index 0000000..02b6765 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/exception/InternalServerErrorExceptionMapper.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.exception; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import it.polito.escape.verify.model.ErrorMessage; + +@Provider +public class InternalServerErrorExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(InternalServerErrorException exception) { + ErrorMessage errorMessage = new ErrorMessage( exception.getMessage(), + 500, + "http://localhost:8080/verify/api-docs/"); + return Response.status(Status.INTERNAL_SERVER_ERROR).entity(errorMessage).build(); + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/model/Configuration.java b/verigraph/src/main/java/it/polito/escape/verify/model/Configuration.java new file mode 100644 index 0000000..3498a10 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/model/Configuration.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.model; + +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import it.polito.escape.verify.deserializer.ConfigurationCustomDeserializer; +import it.polito.escape.verify.serializer.CustomConfigurationSerializer; + +@XmlRootElement +@ApiModel("Configuration") +@JsonSerialize(using = CustomConfigurationSerializer.class) +@JsonDeserialize(using = ConfigurationCustomDeserializer.class) +public class Configuration { + + @ApiModelProperty(required = false, hidden = true) + @XmlTransient + private String id; + + @ApiModelProperty(required = false) + @XmlTransient + private String description = ""; + + @ApiModelProperty(required = true) + private JsonNode configuration; + + public Configuration() { + + } + + public Configuration(String id, String description, JsonNode configuration) { + this.id = id; + this.description = description; + this.configuration = configuration; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public JsonNode getConfiguration() { + return configuration; + } + + public void setConfiguration(JsonNode configuration) { + this.configuration = configuration; + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/model/Entry.java b/verigraph/src/main/java/it/polito/escape/verify/model/Entry.java new file mode 100644 index 0000000..f2fdf14 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/model/Entry.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.model; + +public class Entry { + private String direction; + private String destination; + + public Entry(String direction, String destination) { + this.direction = direction; + this.destination = destination; + } + + public String getDirection() { + return direction; + } + + public void setDirection(String direction) { + this.direction = direction; + } + + public String getDestination() { + return destination; + } + + public void setDestination(String destination) { + this.destination = destination; + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/model/ErrorMessage.java b/verigraph/src/main/java/it/polito/escape/verify/model/ErrorMessage.java new file mode 100644 index 0000000..e0e79d3 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/model/ErrorMessage.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.model; + +import javax.xml.bind.annotation.XmlRootElement; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "Error") +@XmlRootElement +public class ErrorMessage { + + @ApiModelProperty(example = "Error message") + private String errorMessage; + @ApiModelProperty(allowableValues = "400,403,404,500", value = "HTTP error code", example = "[400,403,404,500]") + private int errorCode; + @ApiModelProperty(example = "http://localhost:8080/verify/api-docs/") + private String documentation; + + public ErrorMessage() { + + } + + public ErrorMessage(String errorMessage, int errorCode, String documentation) { + this.errorMessage = errorMessage; + this.errorCode = errorCode; + this.documentation = documentation; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public int getErrorCode() { + return errorCode; + } + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + public String getDocumentation() { + return documentation; + } + + public void setDocumentation(String documentation) { + this.documentation = documentation; + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/model/Graph.java b/verigraph/src/main/java/it/polito/escape/verify/model/Graph.java new file mode 100644 index 0000000..44a2273 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/model/Graph.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.model; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import it.polito.escape.verify.deserializer.GraphCustomDeserializer; +import it.polito.escape.verify.serializer.CustomMapSerializer; + +@ApiModel(value = "Graph") +@XmlRootElement +@JsonDeserialize(using = GraphCustomDeserializer.class) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class Graph { + + @ApiModelProperty(required = false, hidden = true) + @XmlTransient + private long id; + + @ApiModelProperty(name = "nodes", notes = "Nodes", dataType = "List[it.polito.escape.verify.model.Node]") + private Map nodes = new HashMap(); + + @ApiModelProperty(required = false, hidden = true) + @XmlTransient + private Set links = new HashSet(); + + public Graph() { + + } + + public Graph(long id) { + this.id = id; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @JsonSerialize(using = CustomMapSerializer.class) + public Map getNodes() { + return nodes; + } + + public void setNodes(Map nodes) { + this.nodes = nodes; + } + + @XmlTransient + public Set getLinks() { + return links; + } + + public void setLinks(Set links) { + this.links = links; + } + + public void addLink(String url, String rel) { + Link link = new Link(); + link.setLink(url); + link.setRel(rel); + links.add(link); + } + + public Node searchNodeByName(String name) { + for (Node node : this.nodes.values()) { + if (node.getName().equals(name)) + return node; + } + return null; + } + + public int nodesWithName(String name) { + int occurrences = 0; + for (Node node : this.nodes.values()) { + if (node.getName().equals(name)) + occurrences++; + + } + return occurrences; + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/model/Link.java b/verigraph/src/main/java/it/polito/escape/verify/model/Link.java new file mode 100644 index 0000000..dc2b4c5 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/model/Link.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.model; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "Link") +public class Link { + @ApiModelProperty(required = false, hidden = true) + private String link; + @ApiModelProperty(required = false, hidden = true) + private String rel; + + public String getLink() { + return link; + } + + public void setLink(String link) { + this.link = link; + } + + public String getRel() { + return rel; + } + + public void setRel(String rel) { + this.rel = rel; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((link == null) ? 0 : link.hashCode()); + result = prime * result + ((rel == null) ? 0 : rel.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Link other = (Link) obj; + if (link == null) { + if (other.link != null) + return false; + } + else if (!link.equals(other.link)) + return false; + if (rel == null) { + if (other.rel != null) + return false; + } + else if (!rel.equals(other.rel)) + return false; + return true; + } + +} \ No newline at end of file diff --git a/verigraph/src/main/java/it/polito/escape/verify/model/Neighbour.java b/verigraph/src/main/java/it/polito/escape/verify/model/Neighbour.java new file mode 100644 index 0000000..4b1206c --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/model/Neighbour.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.model; + +import javax.xml.bind.annotation.XmlTransient; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "Neighbour") +public class Neighbour { + + @ApiModelProperty(required = false, hidden = true) + @XmlTransient + private long id; + + @ApiModelProperty( required = true, + example = "nat", + value = "The neighbour name must refer to an existing node of the same graph") + private String name; + + public Neighbour() { + + } + + public Neighbour(long id, String name) { + this.id = id; + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + else + return false; + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/model/Node.java b/verigraph/src/main/java/it/polito/escape/verify/model/Node.java new file mode 100644 index 0000000..9f667ce --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/model/Node.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.model; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import it.polito.escape.verify.deserializer.NodeCustomDeserializer; +import it.polito.escape.verify.serializer.CustomMapSerializer; + +@ApiModel(value = "Node") +@XmlRootElement +@JsonDeserialize(using = NodeCustomDeserializer.class) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class Node { + + @ApiModelProperty(required = false, hidden = true) + @XmlTransient + private long id; + + @ApiModelProperty(required = true, example = "ep", value = "The name of the node can be any string") + private String name; + + @ApiModelProperty( required = true, + example = "endpoint", + value = "The functional types that are currently supported are: endpoint, firewall, nat, antispam, webclient, webserver, mailclient, mailserver") + private String functional_type; + + @ApiModelProperty(required = false, hidden = true) + @XmlTransient + private Configuration configuration = new Configuration(); + + @ApiModelProperty( name = "neighbours", + notes = "Neighbours", + dataType = "List[it.polito.escape.verify.model.Neighbour]") + private Map neighbours = new HashMap(); + + @ApiModelProperty(required = false, hidden = true) + @XmlTransient + private Set links = new HashSet<>(); + + public Node() { + + } + + public Node(long id, String name, String functional_type, Configuration configuration) { + this.id = id; + this.name = name; + this.functional_type = functional_type; + this.configuration = configuration; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFunctional_type() { + return functional_type; + } + + public void setFunctional_type(String functional_type) { + this.functional_type = functional_type; + } + + // @XmlTransient + public Configuration getConfiguration() { + return configuration; + } + + public void setConfiguration(Configuration configuration) { + this.configuration = configuration; + } + + @JsonSerialize(using = CustomMapSerializer.class) + public Map getNeighbours() { + return neighbours; + } + + public void setNeighbours(Map neighbours) { + this.neighbours = neighbours; + } + + public long getId() { + return this.id; + } + + public void setId(long id) { + this.id = id; + } + + public Set getLinks() { + return links; + } + + public void setLinks(Set links) { + this.links = links; + } + + public void addLink(String url, String rel) { + Link link = new Link(); + link.setLink(url); + link.setRel(rel); + links.add(link); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + else + return false; + } + + public Neighbour searchNeighbourByName(String name) { + for (Neighbour neighbour : this.neighbours.values()) { + if (neighbour.getName().equals(name)) + return neighbour; + } + return null; + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/model/Test.java b/verigraph/src/main/java/it/polito/escape/verify/model/Test.java new file mode 100644 index 0000000..1127abc --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/model/Test.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.model; + +import java.util.ArrayList; +import java.util.List; + +public class Test { + private List nodes = new ArrayList(); + private String result; + + public Test() { + + } + + public Test(List paths, int result) { + switch (result) { + case 0: + this.result = "SAT"; + break; + case -1: + this.result = "UNSAT"; + break; + case -2: + this.result = "UNKNOWN"; + break; + default: + this.result = "UNKNWON"; + break; + } + this.nodes = paths; + } + + public Test(List paths, String result) { + this.nodes = paths; + this.result = result; + } + + public List getPath() { + return nodes; + } + + public void setPath(List paths) { + this.nodes = paths; + } + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/model/Verification.java b/verigraph/src/main/java/it/polito/escape/verify/model/Verification.java new file mode 100644 index 0000000..60b4ddb --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/model/Verification.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.model; + +import java.util.ArrayList; +import java.util.List; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "Policy verification") +public class Verification { + + @ApiModelProperty(example = "SAT | UNSAT | UNKNOWN") + private String result; + private String comment; + private List tests = new ArrayList(); + + public Verification() { + + } + + public Verification(String result) { + this.result = result; + } + + public Verification(String result, List tests, String comment){ + this.result = result; + this.tests = tests; + this.comment = comment; + } + + public Verification(String result, String comment){ + this.result = result; + this.comment = comment; + } + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + + public List getTests() { + return tests; + } + + public void setTests(List tests) { + this.tests = tests; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/model/jaxb.properties b/verigraph/src/main/java/it/polito/escape/verify/model/jaxb.properties new file mode 100644 index 0000000..5837a4c --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/model/jaxb.properties @@ -0,0 +1 @@ +javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory \ No newline at end of file diff --git a/verigraph/src/main/java/it/polito/escape/verify/resources/GraphResource.java b/verigraph/src/main/java/it/polito/escape/verify/resources/GraphResource.java new file mode 100644 index 0000000..e610f1d --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/resources/GraphResource.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.resources; + +import java.net.URI; +import java.util.List; + +import javax.ws.rs.BeanParam; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import it.polito.escape.verify.model.ErrorMessage; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Verification; +import it.polito.escape.verify.resources.beans.VerificationBean; +import it.polito.escape.verify.service.GraphService; +import it.polito.escape.verify.service.VerificationService; + +@Path("/graphs") +@Api(value = "/graphs", description = "Manage graphs") +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public class GraphResource { + GraphService graphService = new GraphService(); + VerificationService verificationService = new VerificationService(); + + @GET + @ApiOperation( httpMethod = "GET", + value = "Returns all graphs", + notes = "Returns an array of graphs", + response = Graph.class, + responseContainer = "List") + @ApiResponses(value = { @ApiResponse( code = 200, + message = "All the graphs have been returned in the message body", + response = Graph.class, + responseContainer = "List"), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class)}) + public List getGraphs() { + return graphService.getAllGraphs(); + } + + @POST + @ApiOperation( httpMethod = "POST", + value = "Creates a graph", + notes = "Creates a signle graph", + response = Response.class) + @ApiResponses(value = { @ApiResponse(code = 400, message = "Invalid graph supplied", response = ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse(code = 201, message = "Graph successfully created", response = Graph.class) }) + public Response addGraph( @ApiParam(value = "New graph object", required = true) Graph graph, + @Context UriInfo uriInfo) { + Graph newGraph = graphService.addGraph(graph); + String newId = String.valueOf(newGraph.getId()); + URI uri = uriInfo.getAbsolutePathBuilder().path(newId).build(); + return Response.created(uri).entity(newGraph).build(); + } + + @GET + @Path("/{graphId}") + @ApiOperation( httpMethod = "GET", + value = "Returns a graph", + notes = "Returns a signle graph", + response = Graph.class) + @ApiResponses(value = { @ApiResponse(code = 403, message = "Invalid graph id", response = ErrorMessage.class), + @ApiResponse(code = 404, message = "Graph not found", response = ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse( code = 200, + message = "The requested graph has been returned in the message body", + response = Graph.class) }) + public Graph getGraph( @ApiParam(value = "Graph id", required = true) @PathParam("graphId") long graphId, + @Context UriInfo uriInfo) { + Graph graph = graphService.getGraph(graphId); + graph.addLink(getUriForSelf(uriInfo, graph), "self"); + graph.addLink(getUriForNodes(uriInfo, graph), "nodes"); + return graph; + } + + @PUT + @Path("/{graphId}") + @ApiOperation(httpMethod = "PUT", value = "Edits a graph", notes = "Edits a single graph", response = Graph.class) + @ApiResponses(value = { @ApiResponse(code = 400, message = "Invalid graph object", response = ErrorMessage.class), + @ApiResponse(code = 403, message = "Invalid graph id", response = ErrorMessage.class), + @ApiResponse(code = 404, message = "Graph not found", response = ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse(code = 200, message = "Graph edited successfully", response = Graph.class) }) + public Graph updateGraph( @ApiParam(value = "Graph id", required = true) @PathParam("graphId") long id, + @ApiParam(value = "Updated graph object", required = true) Graph graph) { + graph.setId(id); + return graphService.updateGraph(graph); + } + + @DELETE + @Path("/{graphId}") + @ApiOperation(httpMethod = "DELETE", value = "Deletes a graph", notes = "Deletes a signle graph") + @ApiResponses(value = { @ApiResponse(code = 403, message = "Invalid graph id", response = ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse(code = 204, message = "Graph successfully deleted") }) + public void deleteGraph(@ApiParam(value = "Graph id", required = true) @PathParam("graphId") long id) { + graphService.removeGraph(id); + } + + @GET + @Path("/{graphId}/policy") + @ApiOperation( httpMethod = "GET", + value = "Verifies a given policy in a graph", + notes = "In order to verify a given policy (e.g. 'reachability') all nodes of the desired graph must have a valid configuration.") + @ApiResponses(value = { @ApiResponse( code = 403, + message = "Invalid graph id or invalid configuration for source and/or destination node", + response = ErrorMessage.class), + @ApiResponse( code = 404, + message = "Graph not found or source node not found or destination node not found or configuration for source and/or destination node not available", + response = ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class),}) + public Verification verifyGraph(@ApiParam(value = "Graph id", required = true) @PathParam("graphId") long graphId, + @ApiParam( value = "'source' and 'destination' must refer to names of existing nodes in the same graph, 'type' refers to the required verification between the two (e.g. 'reachability')", + required = true) @BeanParam VerificationBean verificationBean) { + + return verificationService.verify(graphId, verificationBean); + } + + private String getUriForSelf(UriInfo uriInfo, Graph graph) { + String uri = uriInfo.getBaseUriBuilder() + .path(GraphResource.class) + .path(Long.toString(graph.getId())) + .build() + .toString(); + return uri; + } + + private String getUriForNodes(UriInfo uriInfo, Graph graph) { + String uri = uriInfo.getBaseUriBuilder() + .path(GraphResource.class) + .path(GraphResource.class, "getNodeResource") + // .path(NodeResource.class) + .resolveTemplate("graphId", graph.getId()) + .build() + .toString(); + return uri; + } + + @Path("/{graphId}/nodes") + public NodeResource getNodeResource() { + return new NodeResource(); + } +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/resources/NeighbourResource.java b/verigraph/src/main/java/it/polito/escape/verify/resources/NeighbourResource.java new file mode 100644 index 0000000..63376da --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/resources/NeighbourResource.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.resources; + +import java.net.URI; +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import it.polito.escape.verify.model.ErrorMessage; +import it.polito.escape.verify.model.Neighbour; +import it.polito.escape.verify.service.NeighbourService; + +//@Path("/") +@Api( hidden= true, value = "", description = "Manage neighbours" ) +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class NeighbourResource { + private NeighbourService neighboursService = new NeighbourService(); + + @GET + @ApiOperation( + httpMethod = "GET", + value = "Returns all neighbours of a given node belonging to a given graph", + notes = "Returns an array of neighbours of a given node belonging to a given graph", + response = Neighbour.class, + responseContainer = "List") + @ApiResponses(value = { @ApiResponse(code = 403, message = "Invalid graph and/or node id", response=ErrorMessage.class), + @ApiResponse(code = 404, message = "Graph and/or node not found", response=ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse(code = 200, message = "All the neighbours have been returned in the message body", response=Neighbour.class, responseContainer="List")}) + public List getAllNeighbours( + @ApiParam(value = "Graph id", required = true) @PathParam("graphId") long graphId, + @ApiParam(value = "Node id", required = true) @PathParam("nodeId") long nodeId){ + return neighboursService.getAllNeighbours(graphId, nodeId); + } + + @POST + @ApiOperation( + httpMethod = "POST", + value = "Adds a neighbour to a given node belonging to a given graph", + notes = "Adds single neighbour to a given node belonging to a given graph", + response = Neighbour.class) + @ApiResponses(value = { @ApiResponse(code = 403, message = "Invalid graph and/or node id", response=ErrorMessage.class), + @ApiResponse(code = 404, message = "Graph and/or node not found", response=ErrorMessage.class), + @ApiResponse(code = 400, message = "Invalid neighbour object", response=ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse(code = 201, message = "Neighbour successfully created", response=Neighbour.class)}) + public Response addNeighbour( + @ApiParam(value = "Graph id", required = true) @PathParam("graphId") long graphId, + @ApiParam(value = "Node id", required = true) @PathParam("nodeId") long nodeId, + @ApiParam(value = "New neighbour object. Neighbour name must refer to the name of an existing node of the same graph", required = true) Neighbour neighbour, + @Context UriInfo uriInfo){ + Neighbour newNeighbour = neighboursService.addNeighbour(graphId, nodeId, neighbour); + String newId = String.valueOf(newNeighbour.getId()); + URI uri = uriInfo.getAbsolutePathBuilder().path(newId).build(); + return Response.created(uri) + .entity(newNeighbour) + .build(); + } + + @PUT + @Path("{neighbourId}") + @ApiOperation( + httpMethod = "PUT", + value = "Edits a neighbour of a given node belonging to a given graph", + notes = "Edits a single neighbour of a given node belonging to a given graph", + response = Neighbour.class) + @ApiResponses(value = { @ApiResponse(code = 403, message = "Invalid graph and/or node and/or neighbour id", response=ErrorMessage.class), + @ApiResponse(code = 404, message = "Graph and/or node and /or neighbour not found", response=ErrorMessage.class), + @ApiResponse(code = 400, message = "Invalid neighbour object", response=ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse(code = 200, message = "Neighbour edited successfully", response=Neighbour.class)}) + public Neighbour updateNeighbour( + @ApiParam(value = "Graph id", required = true) @PathParam("graphId") long graphId, + @ApiParam(value = "Node id", required = true) @PathParam("nodeId") long nodeId, + @ApiParam(value = "Neighbour id", required = true) @PathParam("neighbourId") long neighbourId, + @ApiParam(value = "Updated neighbour object. Neighbour name must refer to the name of an existing node of the same graph", required = true) Neighbour neighbour){ + neighbour.setId(neighbourId); + return neighboursService.updateNeighbour(graphId, nodeId, neighbour); + } + + @DELETE + @Path("{neighbourId}") + @ApiOperation( + httpMethod = "DELETE", + value = "Removes a neighbour from a given node belonging to a given graph", + notes = "Deletes a single neighbour of a given node belonging to a given graph", + response = Neighbour.class) + @ApiResponses(value = { @ApiResponse(code = 403, message = "Invalid graph and/or node and/or neighbour id", response=ErrorMessage.class), + @ApiResponse(code = 404, message = "Graph and/or node not found", response=ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse(code = 204, message = "Node successfully deleted")}) + public void deleteNeighbour( + @ApiParam(value = "Graph id", required = true) @PathParam("graphId") long graphId, + @ApiParam(value = "Node id", required = true) @PathParam("nodeId") long nodeId, + @ApiParam(value = "Neighbour id", required = true) @PathParam("neighbourId") long neighbourId){ + neighboursService.removeNeighbour(graphId, nodeId, neighbourId); + } + + @GET + @Path("{neighbourId}") + @ApiOperation( + httpMethod = "GET", + value = "Returns a neighbour of a given node belonging to a given graph", + notes = "Returns a single neighbour of a given node belonging to a given graph", + response = Neighbour.class) + @ApiResponses(value = { @ApiResponse(code = 403, message = "Invalid graph and/or node and/or neighbour id", response=ErrorMessage.class), + @ApiResponse(code = 404, message = "Graph and/or node and /or neighbour not found", response=ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse(code = 200, message = "The requested neighbour has been returned in the message body", response=Neighbour.class)}) + public Neighbour getNeighbour( + @ApiParam(value = "Graph id", required = true) @PathParam("graphId") long graphId, + @ApiParam(value = "Node id", required = true) @PathParam("nodeId") long nodeId, + @ApiParam(value = "Neighbour id", required = true) @PathParam("neighbourId") long neighbourId){ + return neighboursService.getNeighbour(graphId, nodeId, neighbourId); + } +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/resources/NodeResource.java b/verigraph/src/main/java/it/polito/escape/verify/resources/NodeResource.java new file mode 100644 index 0000000..58d4064 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/resources/NodeResource.java @@ -0,0 +1,230 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.resources; + +import java.net.URI; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import it.polito.escape.verify.exception.BadRequestException; +import it.polito.escape.verify.exception.ForbiddenException; +import it.polito.escape.verify.model.Configuration; +import it.polito.escape.verify.model.ErrorMessage; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Neighbour; +import it.polito.escape.verify.model.Node; +import it.polito.escape.verify.service.GraphService; +import it.polito.escape.verify.service.NodeService; + +@Api( hidden= true, value = "", description = "Manage nodes" ) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public class NodeResource { + + NodeService nodeService = new NodeService(); + + + @GET + @ApiOperation( + httpMethod = "GET", + value = "Returns all nodes of a given graph", + notes = "Returns an array of nodes belonging to a given graph", + response = Node.class, + responseContainer = "List") + @ApiResponses(value = { @ApiResponse(code = 403, message = "Invalid graph id", response = ErrorMessage.class), + @ApiResponse(code = 404, message = "Graph not found", response = ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse(code = 200, message = "All the nodes have been returned in the message body", response = Node.class, responseContainer = "List") }) + public List getNodes(@ApiParam(value = "Graph id", required = true) @PathParam("graphId") long graphId){ + return nodeService.getAllNodes(graphId); + } + + @POST + @ApiOperation( + httpMethod = "POST", + value = "Creates a node in a given graph", + notes = "Creates a single node for a given graph", + response = Response.class) + @ApiResponses(value = { @ApiResponse(code = 400, message = "Invalid node supplied", response = ErrorMessage.class), + @ApiResponse(code = 403, message = "Invalid graph id", response = ErrorMessage.class), + @ApiResponse(code = 404, message = "Graph not found", response = ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse(code = 201, message = "Node successfully created", response = Node.class)}) + public Response addNode( + @ApiParam(value = "Graph id", required = true) @PathParam("graphId") long graphId, + @ApiParam(value = "New node object", required = true) Node node, + @Context UriInfo uriInfo) { + Node newNode = nodeService.addNode(graphId, node); + String newId = String.valueOf(newNode.getId()); + URI uri = uriInfo.getAbsolutePathBuilder().path(newId).build(); + return Response.created(uri) + .entity(newNode) + .build(); + } + + @GET + @Path("{nodeId}") + @ApiOperation( + httpMethod = "GET", + value = "Returns a node of a given graph", + notes = "Returns a single node of a given graph", + response = Node.class) + @ApiResponses(value = { @ApiResponse(code = 403, message = "Invalid graph and/or node id", response = ErrorMessage.class), + @ApiResponse(code = 404, message = "Graph and/or node not found", response = ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse(code = 200, message = "The requested node has been returned in the message body", response = Node.class)}) + public Node getNode( + @ApiParam(value = "Graph id", required = true) @PathParam("graphId") long graphId, + @ApiParam(value = "Node id", required = true) @PathParam("nodeId") long nodeId, + @Context UriInfo uriInfo){ + Node node = nodeService.getNode(graphId, nodeId); + node.addLink(getUriForSelf(uriInfo, graphId, node), "self"); + node.addLink(getUriForNeighbours(uriInfo, graphId, node), "neighbours"); + return node; + } + + @PUT + @Path("{nodeId}/configuration") + @ApiOperation( + httpMethod = "PUT", + value = "Adds/edits a configuration to a node of a given graph", + notes = "Configures a node. Once all the nodes of a graph have been configured a given policy can be verified for the graph (e.g. 'reachability' between two nodes).") + @ApiResponses(value = { @ApiResponse(code = 403, message = "Invalid graph and/or node id", response = ErrorMessage.class), + @ApiResponse(code = 404, message = "Graph and/or node not found", response = ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse(code = 200, message = "Configuration updated for the requested node")}) + public void addNodeConfiguration( + @ApiParam(value = "Graph id", required = true) @PathParam("graphId") long graphId, + @ApiParam(value = "Node id", required = true) @PathParam("nodeId") long nodeId, + @ApiParam(value = "Node configuration", required = true) Configuration nodeConfiguration, + @Context UriInfo uriInfo){ + if (graphId <= 0) { + throw new ForbiddenException("Illegal graph id: " + graphId); + } + if (nodeId <= 0) { + throw new ForbiddenException("Illegal node id: " + nodeId); + } + Graph graph = new GraphService().getGraph(graphId); + if (graph == null){ + throw new BadRequestException("Graph with id " + graphId + " not found"); + } + Node node = nodeService.getNode(graphId, nodeId); + if (node == null){ + throw new BadRequestException("Node with id " + nodeId + " not found in graph with id " + graphId); + } + Node nodeCopy = new Node(); + nodeCopy.setId(node.getId()); + nodeCopy.setName(node.getName()); + nodeCopy.setFunctional_type(node.getFunctional_type()); + Map nodes = new HashMap(); + nodes.putAll(node.getNeighbours()); + nodeCopy.setNeighbours(nodes); + nodeConfiguration.setId(nodeCopy.getName()); + nodeCopy.setConfiguration(nodeConfiguration); + + Graph graphCopy = new Graph(); + graphCopy.setId(graph.getId()); + graphCopy.setNodes(new HashMap(graph.getNodes())); + graphCopy.getNodes().remove(node.getId()); + + NodeService.validateNode(graphCopy, nodeCopy); + graph.getNodes().put(nodeId, nodeCopy); + } + + + @PUT + @Path("{nodeId}") + @ApiOperation( + httpMethod = "PUT", + value = "Edits a node of a given graph", + notes = "Edits a single node of a given graph", + response = Node.class) + @ApiResponses(value = { @ApiResponse(code = 400, message = "Invalid node object", response = ErrorMessage.class), + @ApiResponse(code = 403, message = "Invalid graph and/or node id", response = ErrorMessage.class), + @ApiResponse(code = 404, message = "Graph and/or node not found", response = ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse(code = 200, message = "Node edited successfully", response = Node.class)}) + public Node updateNode( + @ApiParam(value = "Graph id", required = true) @PathParam("graphId") long graphId, + @ApiParam(value = "Node id", required = true) @PathParam("nodeId") long nodeId, + @ApiParam(value = "Updated node object", required = true) Node node){ + node.setId(nodeId); + return nodeService.updateNode(graphId, node); + } + + @DELETE + @Path("{nodeId}") + @ApiOperation( + httpMethod = "DELETE", + value = "Deletes a node of a given graph", + notes = "Deletes a single node of a given graph") + @ApiResponses(value = { @ApiResponse(code = 403, message = "Invalid graph and/or node id", response = ErrorMessage.class), + @ApiResponse(code = 500, message = "Internal server error", response = ErrorMessage.class), + @ApiResponse(code = 204, message = "Node successfully deleted")}) + public void deleteNode( + @ApiParam(value = "Graph id", required = true) @PathParam("graphId") long graphId, + @ApiParam(value = "Node id", required = true) @PathParam("nodeId") long nodeId){ + nodeService.removeNode(graphId, nodeId); + } + + private String getUriForSelf(UriInfo uriInfo, long graphId, Node node) { + String uri = uriInfo.getBaseUriBuilder() + //.path(NodeResource.class) + .path(GraphResource.class) + .path(GraphResource.class, "getNodeResource") + .resolveTemplate("graphId", graphId) + .path(Long.toString(node.getId())) + .build() + .toString(); + return uri; + } + + private String getUriForNeighbours(UriInfo uriInfo, long graphId, Node node) { + String uri = uriInfo.getBaseUriBuilder() + .path(GraphResource.class) + .path(GraphResource.class, "getNodeResource") + .resolveTemplate("graphId", graphId) + .path(Long.toString(node.getId())) + .path("neighbours") + .build() + .toString(); +// .path(NodeResource.class) +// .path(NodeResource.class, "getNeighbourResource") +// .path(NeighbourResource.class) +// .resolveTemplate("nodeId", node.getId()) +// .build() +// .toString(); + return uri; + } + + @Path("{nodeId}/neighbours") + public NeighbourResource getNeighbourResource(){ + return new NeighbourResource(); + } +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/resources/beans/VerificationBean.java b/verigraph/src/main/java/it/polito/escape/verify/resources/beans/VerificationBean.java new file mode 100644 index 0000000..d6f9ca6 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/resources/beans/VerificationBean.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.resources.beans; + +import javax.ws.rs.QueryParam; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel("Verification") +public class VerificationBean { + + @ApiModelProperty(example = "webclient", value = "Source node. Must refer to an existing node of the same graph") + private @QueryParam("source") String source; + + @ApiModelProperty( example = "webserver", + value = "Destination node. Must refer to an existing node of the same graph") + private @QueryParam("destination") String destination; + + @ApiModelProperty( example = "reachability", + value = "Verification policy ('reachability', 'isolation', 'traversal')") + private @QueryParam("type") String type; + + @ApiModelProperty( example = "firewall", + value = "Absent if verification type is 'reachability', equal to the name of a middlebox to be avoided if verification type is 'isolation', equal to the name of a middlebox to be traversed if verification type is 'traversal'") + private @QueryParam("middlebox") String middlebox; + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getDestination() { + return destination; + } + + public void setDestination(String destination) { + this.destination = destination; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getMiddlebox() { + return middlebox; + } + + public void setMiddlebox(String middlebox) { + this.middlebox = middlebox; + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/serializer/CustomConfigurationSerializer.java b/verigraph/src/main/java/it/polito/escape/verify/serializer/CustomConfigurationSerializer.java new file mode 100644 index 0000000..e28273f --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/serializer/CustomConfigurationSerializer.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.serializer; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import it.polito.escape.verify.exception.InternalServerErrorException; +import it.polito.escape.verify.model.Configuration; + +public class CustomConfigurationSerializer extends JsonSerializer { + + @Override + public void serialize(Configuration conf, JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonProcessingException { + try { + jgen.writeObject(conf.getConfiguration()); + } catch (IOException e) { + throw new InternalServerErrorException("I/O error serializing a configuration object: " + e.getMessage()); + } + + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/serializer/CustomMapSerializer.java b/verigraph/src/main/java/it/polito/escape/verify/serializer/CustomMapSerializer.java new file mode 100644 index 0000000..e811365 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/serializer/CustomMapSerializer.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.serializer; + +import java.io.IOException; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import it.polito.escape.verify.exception.InternalServerErrorException; + +public class CustomMapSerializer extends JsonSerializer> { + @Override + public void serialize(final Map value, final JsonGenerator jgen, final SerializerProvider provider) { + try { + jgen.writeObject(value.values()); + } catch (IOException e) { + throw new InternalServerErrorException("I/O error serializing a map: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/verigraph/src/main/java/it/polito/escape/verify/service/GraphService.java b/verigraph/src/main/java/it/polito/escape/verify/service/GraphService.java new file mode 100644 index 0000000..b34eb08 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/service/GraphService.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import it.polito.escape.verify.database.DatabaseClass; +import it.polito.escape.verify.exception.DataNotFoundException; +import it.polito.escape.verify.exception.ForbiddenException; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Neighbour; +import it.polito.escape.verify.model.Node; + +public class GraphService { + + private Map graphs = DatabaseClass.getInstance().getGraphs(); + + public GraphService() { + + } + + public List getAllGraphs() { + return new ArrayList(graphs.values()); + } + + public Graph getGraph(long id) { + if (id <= 0) { + throw new ForbiddenException("Illegal graph id: " + id); + } + Graph graph = graphs.get(id); + if (graph == null) { + throw new DataNotFoundException("Graph with id " + id + " not found"); + } + return graph; + } + + public Graph updateGraph(Graph graph) { + if (graph.getId() <= 0) { + throw new ForbiddenException("Illegal graph id: " + graph.getId()); + } + Graph localGraph = graphs.get(graph.getId()); + if (localGraph == null) { + throw new DataNotFoundException("Graph with id " + graph.getId() + " not found"); + } + + validateGraph(graph); + +// int numberOfNodes = 0; +// for (Node node : graph.getNodes().values()) { +// +// node.setId(++numberOfNodes); +// +// int numberOfNodeNeighbours = 0; +// for (Neighbour neighbour : node.getNeighbours().values()) { +// neighbour.setId(++numberOfNodeNeighbours); +// } +// } + + for (Map.Entry nodeEntry : graph.getNodes().entrySet()){ + nodeEntry.getValue().setId(nodeEntry.getKey()); + + for (Map.Entry neighbourEntry : nodeEntry.getValue().getNeighbours().entrySet()){ + neighbourEntry.getValue().setId(neighbourEntry.getKey()); + } + } + + synchronized(this){ + graphs.put(graph.getId(), graph); + DatabaseClass.persistDatabase(); + return graph; + } + } + + public Graph removeGraph(long id) { + if (id <= 0) { + throw new ForbiddenException("Illegal graph id: " + id); + } + synchronized(this){ + return graphs.remove(id); + } + } + + public Graph addGraph(Graph graph) { + validateGraph(graph); + + synchronized (this) { + graph.setId(DatabaseClass.getInstance().getNumberOfGraphs() + 1); + } +// int numberOfNodes = 0; +// for (Node node : graph.getNodes().values()) { +// +// node.setId(++numberOfNodes); +// +// int numberOfNodeNeighbours = 0; +// for (Neighbour neighbour : node.getNeighbours().values()) { +// neighbour.setId(++numberOfNodeNeighbours); +// } +// } + + for (Map.Entry nodeEntry : graph.getNodes().entrySet()){ + nodeEntry.getValue().setId(nodeEntry.getKey()); + + for (Map.Entry neighbourEntry : nodeEntry.getValue().getNeighbours().entrySet()){ + neighbourEntry.getValue().setId(neighbourEntry.getKey()); + } + } + + synchronized(this){ + graphs.put(graph.getId(), graph); + DatabaseClass.persistDatabase(); + return graph; + } + } + + public static void validateGraph(Graph graph) { + for (Node node : graph.getNodes().values()) { + NodeService.validateNode(graph, node); + } + } +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/service/JsonValidationService.java b/verigraph/src/main/java/it/polito/escape/verify/service/JsonValidationService.java new file mode 100644 index 0000000..1ac6c1f --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/service/JsonValidationService.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.service; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Iterator; +import java.util.Map.Entry; + +import org.apache.commons.lang3.text.WordUtils; + +import com.fasterxml.jackson.databind.JsonNode; + +import it.polito.escape.verify.exception.BadRequestException; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Node; + +public class JsonValidationService { + + private Graph graph = new Graph(); + + private Node node = new Node(); + + public JsonValidationService() { + + } + + public JsonValidationService(Graph graph, Node node) { + this.graph = graph; + this.node = node; + } + + public boolean validateFieldAgainstNodeNames(String value) { + for (Node node : this.graph.getNodes().values()) { + if (node.getName().equals(value)) + return true; + } + return false; + } + + public void validateFieldsAgainstNodeNames(JsonNode node) { + if (node.isTextual()) { + boolean isValid = validateFieldAgainstNodeNames(node.asText()); + if (!isValid) { + System.out.println(node.asText() + " is not a valid string!"); + throw new BadRequestException("String '" + node.asText() + + "' is not valid for the configuration of node '" + this.node.getName() + + "'"); + } + } + if (node.isArray()) { + for (JsonNode object : node) { + validateFieldsAgainstNodeNames(object); + } + } + if (node.isObject()) { + Iterator> iter = node.fields(); + + while (iter.hasNext()) { + Entry item = iter.next(); + validateFieldsAgainstNodeNames(item.getValue()); + } + } + + } + + public boolean validateNodeConfiguration() { + String className = WordUtils.capitalize(node.getFunctional_type()) + "Validator"; + + Class validator; + try { + validator = Class.forName("it.polito.escape.verify.validation." + className); + } + catch (ClassNotFoundException e) { + System.out.println(className + " not found, configuration properties of node '" + node.getName() + + "' will be validated against node names"); + return false; + } + + Class graphClass; + Class nodeClass; + Class configurationClass; + try { + graphClass = Class.forName("it.polito.escape.verify.model.Graph"); + nodeClass = Class.forName("it.polito.escape.verify.model.Node"); + configurationClass = Class.forName("it.polito.escape.verify.model.Configuration"); + } + catch (ClassNotFoundException e) { + throw new RuntimeException("Model classes not found"); + } + + Class[] paramTypes = new Class[3]; + paramTypes[0] = graphClass; + paramTypes[1] = nodeClass; + paramTypes[2] = configurationClass; + + String methodName = "validate"; + + Object instance; + try { + instance = validator.newInstance(); + } + catch (InstantiationException e) { + throw new RuntimeException("'" + className + "' cannot be instantiated"); + } + catch (IllegalAccessException e) { + throw new RuntimeException("Illegal access to '" + className + "' instantiation"); + } + + Method myMethod; + try { + myMethod = validator.getDeclaredMethod(methodName, paramTypes); + } + catch (NoSuchMethodException e) { + throw new RuntimeException("'" + methodName + "' method has to be implemented in " + className + " class"); + } + try { + myMethod.invoke(instance, graph, node, node.getConfiguration()); + } + catch (IllegalAccessException e) { + throw new RuntimeException("Illegal access to '" + methodName + "' method in " + className + " instance"); + } + catch (InvocationTargetException e) { + throw new BadRequestException("Validation failed for node '" + node.getName() + "': " + + e.getTargetException().getMessage()); + } + return true; + } +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/service/NeighbourService.java b/verigraph/src/main/java/it/polito/escape/verify/service/NeighbourService.java new file mode 100644 index 0000000..ecf4e69 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/service/NeighbourService.java @@ -0,0 +1,182 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import it.polito.escape.verify.database.DatabaseClass; +import it.polito.escape.verify.exception.BadRequestException; +import it.polito.escape.verify.exception.DataNotFoundException; +import it.polito.escape.verify.exception.ForbiddenException; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Neighbour; +import it.polito.escape.verify.model.Node; + +public class NeighbourService { + + private Map graphs = DatabaseClass.getInstance().getGraphs(); + + public List getAllNeighbours(long graphId, long nodeId) { + if (graphId <= 0) { + throw new ForbiddenException("Illegal graph id: " + graphId); + } + if (nodeId <= 0) { + throw new ForbiddenException("Illegal node id: " + nodeId); + } + Graph graph = graphs.get(graphId); + if (graph == null) + throw new DataNotFoundException("Graph with id " + graphId + " not found"); + Map nodes = graph.getNodes(); + Node node = nodes.get(nodeId); + if (node == null) + throw new DataNotFoundException("Node with id " + nodeId + " not found in graph with id " + graphId); + Map neighbours = node.getNeighbours(); + return new ArrayList(neighbours.values()); + } + + public Neighbour getNeighbour(long graphId, long nodeId, long neighbourId) { + if (graphId <= 0) { + throw new ForbiddenException("Illegal graph id: " + graphId); + } + if (nodeId <= 0) { + throw new ForbiddenException("Illegal node id: " + nodeId); + } + if (neighbourId <= 0) { + throw new ForbiddenException("Illegal neighbour id: " + neighbourId); + } + Graph graph = graphs.get(graphId); + if (graph == null) + throw new DataNotFoundException("Graph with id " + graphId + " not found"); + Map nodes = graph.getNodes(); + Node node = nodes.get(nodeId); + if (node == null) { + throw new DataNotFoundException("Node with id " + nodeId + " not found in graph with id " + graphId); + } + Map neighbours = node.getNeighbours(); + Neighbour neighbour = neighbours.get(neighbourId); + if (neighbour == null) { + throw new DataNotFoundException("Neighbour with id " + neighbourId + " not found for node with id " + nodeId + + " in graph with id " + graphId); + } + return neighbour; + } + + public Neighbour addNeighbour(long graphId, long nodeId, Neighbour neighbour) { + if (graphId <= 0) { + throw new ForbiddenException("Illegal graph id: " + graphId); + } + if (nodeId <= 0) { + throw new ForbiddenException("Illegal node id: " + nodeId); + } + Graph graph = graphs.get(graphId); + if (graph == null) + throw new DataNotFoundException("Graph with id " + graphId + " not found"); + Map nodes = graph.getNodes(); + Node node = nodes.get(nodeId); + if (node == null) { + throw new DataNotFoundException("Node with id " + nodeId + " not found in graph with id " + graphId); + } + Map neighbours = node.getNeighbours(); + + validateNeighbour(graph, node, neighbour); + + synchronized (this) { + neighbour.setId(neighbours.size() + 1); + neighbours.put(neighbour.getId(), neighbour); + DatabaseClass.persistDatabase(); + return neighbour; + } + } + + public Neighbour updateNeighbour(long graphId, long nodeId, Neighbour neighbour) { + if (graphId <= 0) { + throw new ForbiddenException("Illegal graph id: " + graphId); + } + if (nodeId <= 0) { + throw new ForbiddenException("Illegal node id: " + nodeId); + } + if (neighbour.getId() <= 0) { + throw new ForbiddenException("Illegal neighbour id: " + nodeId); + } + Graph graph = graphs.get(graphId); + if (graph == null) + throw new DataNotFoundException("Graph with id " + graphId + " not found"); + Map nodes = graph.getNodes(); + Node node = nodes.get(nodeId); + if (node == null) { + throw new DataNotFoundException("Node with id " + nodeId + " not found in graph with id " + graphId); + } + Map neighbours = node.getNeighbours(); + Neighbour currentNeighbour = neighbours.get(neighbour.getId()); + if (currentNeighbour == null) { + throw new DataNotFoundException("Neighbour with id " + neighbour.getId() + " not found for node with id " + + nodeId + " in graph with id " + graphId); + } + + validateNeighbour(graph, node, neighbour); + + synchronized (this) { + neighbours.put(neighbour.getId(), neighbour); + DatabaseClass.persistDatabase(); + return neighbour; + } + } + + public Neighbour removeNeighbour(long graphId, long nodeId, long neighbourId) { + if (graphId <= 0) { + throw new ForbiddenException("Illegal graph id: " + graphId); + } + if (nodeId <= 0) { + throw new ForbiddenException("Illegal node id: " + nodeId); + } + if (neighbourId <= 0) { + throw new ForbiddenException("Illegal neighbour id: " + nodeId); + } + Graph graph = graphs.get(graphId); + if (graph == null) + throw new DataNotFoundException("Graph with id " + graphId + " not found"); + Map nodes = graph.getNodes(); + Node node = nodes.get(nodeId); + if (node == null) { + throw new DataNotFoundException("Node with id " + nodeId + " not found in graph with id " + graphId); + } + Map neighbours = node.getNeighbours(); + + synchronized(this){ + return neighbours.remove(neighbourId); + } + } + + public static void validateNeighbour(Graph graph, Node node, Neighbour neighbour) { + if (graph == null) + throw new BadRequestException("Neighbour validation failed: cannot validate null graph"); + if (node == null) + throw new BadRequestException("Neighbour validation failed: cannot validate null node"); + if (neighbour == null) + throw new BadRequestException("Neighbour validation failed: cannot validate null neighbour"); + + if (neighbour.getName() == null) + throw new BadRequestException("Neighbour validation failed: neighbour 'name' field cannot be null"); + if (neighbour.getName().equals("")) + throw new BadRequestException("Neighbour validation failed: neighbour 'name' field cannot be an empty string"); + + Node nodeFound = graph.searchNodeByName(neighbour.getName()); + if ((nodeFound == null) || (nodeFound.getName().equals(node.getName()))) + throw new BadRequestException("Neighbour validation failed: '" + neighbour.getName() + + "' is not a valid name for a neighbour of node '" + node.getName() + "'"); + + Neighbour neighbourFound = node.searchNeighbourByName(neighbour.getName()); + if ((neighbourFound != null) && (neighbourFound.equals(neighbour) == false)) + throw new BadRequestException("Neighbour validation failed: node '" + node.getName() + + "' already has a neighbour named '" + neighbour.getName() + "'"); + } +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/service/NodeService.java b/verigraph/src/main/java/it/polito/escape/verify/service/NodeService.java new file mode 100644 index 0000000..e6ad672 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/service/NodeService.java @@ -0,0 +1,258 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.service; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; + +import javax.ws.rs.InternalServerErrorException; + +import com.fasterxml.jackson.databind.JsonNode; +import com.github.fge.jsonschema.core.exceptions.ProcessingException; +import com.github.fge.jsonschema.main.JsonSchema; + +import it.polito.escape.verify.database.DatabaseClass; +import it.polito.escape.verify.exception.BadRequestException; +import it.polito.escape.verify.exception.DataNotFoundException; +import it.polito.escape.verify.exception.ForbiddenException; +import it.polito.escape.verify.model.Configuration; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Neighbour; +import it.polito.escape.verify.model.Node; + +public class NodeService { + + private Map graphs = DatabaseClass.getInstance().getGraphs(); + + public NodeService() { + + } + + public List getAllNodes(long graphId) { + if (graphId <= 0) { + throw new ForbiddenException("Illegal graph id: " + graphId); + } + Graph graph = graphs.get(graphId); + if (graph == null) + throw new DataNotFoundException("Graph with id " + graphId + " not found"); + Map nodes = graph.getNodes(); + return new ArrayList(nodes.values()); + } + + public Node getNode(long graphId, long nodeId) { + if (graphId <= 0) { + throw new ForbiddenException("Illegal graph id: " + graphId); + } + if (nodeId <= 0) { + throw new ForbiddenException("Illegal node id: " + nodeId); + } + Graph graph = graphs.get(graphId); + if (graph == null) + throw new DataNotFoundException("Graph with id " + graphId + " not found"); + Map nodes = graph.getNodes(); + Node node = nodes.get(nodeId); + if (node == null) { + throw new DataNotFoundException("Node with id " + nodeId + " not found in graph with id " + graphId); + } + return node; + } + + public Node updateNode(long graphId, Node node) { + if (graphId <= 0) { + throw new ForbiddenException("Illegal graph id: " + graphId); + } + if (node.getId() <= 0) { + throw new ForbiddenException("Illegal node id: " + node.getId()); + } + Graph graph = graphs.get(graphId); + if (graph == null) + throw new DataNotFoundException("Graph with id " + graphId + " not found"); + Map nodes = graph.getNodes(); + Node localNode = nodes.get(node.getId()); + if (localNode == null) { + throw new DataNotFoundException("Node with id " + node.getId() + " not found in graph with id " + graphId); + } + + Graph graphCopy = new Graph(); + graphCopy.setId(graph.getId()); + graphCopy.setNodes(new HashMap(graph.getNodes())); + graphCopy.getNodes().remove(node.getId()); + + // int numberOfNeighbours = 0; + // for(Neighbour neighbour : node.getNeighbours().values()){ + // neighbour.setId(++numberOfNeighbours); + // } + + for (Map.Entry neighbourEntry : node.getNeighbours().entrySet()) { + neighbourEntry.getValue().setId(neighbourEntry.getKey()); + } + + validateNode(graphCopy, node); + + synchronized (this) { + nodes.put(node.getId(), node); + DatabaseClass.persistDatabase(); + return node; + } + } + + public Node removeNode(long graphId, long nodeId) { + if (graphId <= 0) { + throw new ForbiddenException("Illegal graph id: " + graphId); + } + if (nodeId <= 0) { + throw new ForbiddenException("Illegal node id: " + nodeId); + } + Graph graph = graphs.get(graphId); + if (graph == null) + throw new DataNotFoundException("Graph with id " + graphId + " not found"); + Map nodes = graph.getNodes(); + + synchronized (this) { + return nodes.remove(nodeId); + } + } + + public Node addNode(long graphId, Node node) { + if (graphId <= 0) { + throw new ForbiddenException("Illegal graph id: " + graphId); + } + Graph graph = graphs.get(graphId); + if (graph == null) + throw new DataNotFoundException("Graph with id " + graphId + " not found"); + Map nodes = graph.getNodes(); + + validateNode(graph, node); + + synchronized (this) { + node.setId(DatabaseClass.getInstance().getGraphNumberOfNodes(graphId) + 1); + } + + // int numberOfNeighbours = 0; + + for (Map.Entry neighbourEntry : node.getNeighbours().entrySet()) { + neighbourEntry.getValue().setId(neighbourEntry.getKey()); + } + + // for (Neighbour neighbour : node.getNeighbours().values()) { + // neighbour.setId(++numberOfNeighbours); + // } + + synchronized (this) { + nodes.put(node.getId(), node); + DatabaseClass.persistDatabase(); + return node; + } + } + + public Node searchByName(long graphId, String nodeName) { + if (graphId <= 0) { + throw new ForbiddenException("Illegal graph id: " + graphId); + } + Graph graph = graphs.get(graphId); + if (graph == null) + throw new DataNotFoundException("Graph with id " + graphId + " not found"); + Map nodes = graph.getNodes(); + + for (Node node : nodes.values()) { + if (node.getName().equals(nodeName)) + return node; + } + return null; + } + + public static void validateNode(Graph graph, Node node) { + if (graph == null) + throw new BadRequestException("Node validation failed: cannot validate null graph"); + if (node == null) + throw new BadRequestException("Node validation failed: cannot validate null node"); + + if (node.getName() == null) + throw new BadRequestException("Node validation failed: node 'name' field cannot be null"); + if (node.getFunctional_type() == null) + throw new BadRequestException("Node validation failed: node 'functional_type' field cannot be null"); + + if (node.getName().equals("")) + throw new BadRequestException("Node validation failed: node 'name' field cannot be an empty string"); + if (node.getFunctional_type().equals("")) + throw new BadRequestException("Node validation failed: node 'functional_type' field cannot be an empty string"); + + Node nodeFound = graph.searchNodeByName(node.getName()); + if ((nodeFound != null) && (nodeFound.equals(node) == false)) + throw new BadRequestException("Node validation failed: graph already has a node named '" + node.getName() + + "'"); + Configuration configuration = node.getConfiguration(); + if (configuration != null) { + JsonNode configurationJsonNode = configuration.getConfiguration(); + // validate configuration against schema file + validateNodeConfigurationAgainstSchemaFile(node, configurationJsonNode); + JsonValidationService jsonValidator = new JsonValidationService(graph, node); + boolean hasCustomValidator = jsonValidator.validateNodeConfiguration(); + if (!hasCustomValidator) { + jsonValidator.validateFieldsAgainstNodeNames(configurationJsonNode); + } + } + + // validate neighbours + Map nodeNeighboursMap = node.getNeighbours(); + if (nodeNeighboursMap == null) + throw new BadRequestException("Node validation failed: node 'neighbours' cannot be null"); + for (Neighbour neighbour : nodeNeighboursMap.values()) { + NeighbourService.validateNeighbour(graph, node, neighbour); + } + } + + public static void validateNodeConfigurationAgainstSchemaFile(Node node, JsonNode configurationJson) { + String schemaFileName = node.getFunctional_type() + ".json"; + + File schemaFile = new File(System.getProperty("catalina.base") + "/webapps/verify/json/" + schemaFileName); + + if (!schemaFile.exists()) { + //if no REST client, try gRPC application + schemaFile = new File("src/main/webapp/json/" + schemaFileName); + + if (!schemaFile.exists()) { + throw new ForbiddenException("Functional type '" + node.getFunctional_type() + + "' is not supported! Please edit 'functional_type' field of node '" + + node.getName() + "'"); + } + } + + JsonSchema schemaNode = null; + try { + schemaNode = ValidationUtils.getSchemaNode(schemaFile); + } + catch (IOException e) { + throw new InternalServerErrorException("Unable to load '" + schemaFileName + "' schema file"); + } + catch (ProcessingException e) { + throw new InternalServerErrorException("Unable to resolve '" + schemaFileName + + "' schema file as a schema node"); + } + + try { + ValidationUtils.validateJson(schemaNode, configurationJson); + } + catch (ProcessingException e) { + throw new BadRequestException("Something went wrong trying to validate node '" + node.getName() + + "' with the following configuration: '" + configurationJson.toString() + + "' against the json schema '" + schemaFile.getName() + "': " + + e.getMessage()); + + } + + } +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/service/ValidationUtils.java b/verigraph/src/main/java/it/polito/escape/verify/service/ValidationUtils.java new file mode 100644 index 0000000..77ef4f7 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/service/ValidationUtils.java @@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.service; + +import java.io.File; +import java.io.IOException; +import java.net.URL; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.fge.jackson.JsonLoader; +import com.github.fge.jsonschema.core.exceptions.ProcessingException; +import com.github.fge.jsonschema.core.report.ProcessingMessage; +import com.github.fge.jsonschema.core.report.ProcessingReport; +import com.github.fge.jsonschema.main.JsonSchema; +import com.github.fge.jsonschema.main.JsonSchemaFactory; + +public class ValidationUtils { + + public static final String JSON_V4_SCHEMA_IDENTIFIER = "http://json-schema.org/draft-04/schema#"; + public static final String JSON_SCHEMA_IDENTIFIER_ELEMENT = "$schema"; + + public static JsonNode getJsonNode(String jsonText) throws IOException { + return JsonLoader.fromString(jsonText); + } // getJsonNode(text) ends + + public static JsonNode getJsonNode(File jsonFile) throws IOException { + return JsonLoader.fromFile(jsonFile); + } // getJsonNode(File) ends + + public static JsonNode getJsonNode(URL url) throws IOException { + return JsonLoader.fromURL(url); + } // getJsonNode(URL) ends + + public static JsonNode getJsonNodeFromResource(String resource) throws IOException { + return JsonLoader.fromResource(resource); + } // getJsonNode(Resource) ends + + public static JsonSchema getSchemaNode(String schemaText) throws IOException, ProcessingException { + final JsonNode schemaNode = getJsonNode(schemaText); + return _getSchemaNode(schemaNode); + } // getSchemaNode(text) ends + + public static JsonSchema getSchemaNode(File schemaFile) throws IOException, ProcessingException { + final JsonNode schemaNode = getJsonNode(schemaFile); + return _getSchemaNode(schemaNode); + } // getSchemaNode(File) ends + + public static JsonSchema getSchemaNode(URL schemaFile) throws IOException, ProcessingException { + final JsonNode schemaNode = getJsonNode(schemaFile); + return _getSchemaNode(schemaNode); + } // getSchemaNode(URL) ends + + public static JsonSchema getSchemaNodeFromResource(String resource) throws IOException, ProcessingException { + final JsonNode schemaNode = getJsonNodeFromResource(resource); + return _getSchemaNode(schemaNode); + } // getSchemaNode() ends + + public static void validateJson(JsonSchema jsonSchemaNode, JsonNode jsonNode) throws ProcessingException { + ProcessingReport report = jsonSchemaNode.validate(jsonNode); + if (!report.isSuccess()) { + for (ProcessingMessage processingMessage : report) { + throw new ProcessingException(processingMessage); + } + } + } // validateJson(Node) ends + + public static boolean isJsonValid(JsonSchema jsonSchemaNode, JsonNode jsonNode) throws ProcessingException { + ProcessingReport report = jsonSchemaNode.validate(jsonNode); + return report.isSuccess(); + } // validateJson(Node) ends + + public static boolean isJsonValid(String schemaText, String jsonText) throws ProcessingException, IOException { + final JsonSchema schemaNode = getSchemaNode(schemaText); + final JsonNode jsonNode = getJsonNode(jsonText); + return isJsonValid(schemaNode, jsonNode); + } // validateJson(Node) ends + + public static boolean isJsonValid(File schemaFile, File jsonFile) throws ProcessingException, IOException { + final JsonSchema schemaNode = getSchemaNode(schemaFile); + final JsonNode jsonNode = getJsonNode(jsonFile); + return isJsonValid(schemaNode, jsonNode); + } // validateJson(Node) ends + + public static boolean isJsonValid(URL schemaURL, URL jsonURL) throws ProcessingException, IOException { + final JsonSchema schemaNode = getSchemaNode(schemaURL); + final JsonNode jsonNode = getJsonNode(jsonURL); + return isJsonValid(schemaNode, jsonNode); + } // validateJson(Node) ends + + public static void validateJson(String schemaText, String jsonText) throws IOException, ProcessingException { + final JsonSchema schemaNode = getSchemaNode(schemaText); + final JsonNode jsonNode = getJsonNode(jsonText); + validateJson(schemaNode, jsonNode); + } // validateJson(text) ends + + public static void validateJson(File schemaFile, File jsonFile) throws IOException, ProcessingException { + final JsonSchema schemaNode = getSchemaNode(schemaFile); + final JsonNode jsonNode = getJsonNode(jsonFile); + validateJson(schemaNode, jsonNode); + } // validateJson(File) ends + + public static void validateJson(URL schemaDocument, URL jsonDocument) throws IOException, ProcessingException { + final JsonSchema schemaNode = getSchemaNode(schemaDocument); + final JsonNode jsonNode = getJsonNode(jsonDocument); + validateJson(schemaNode, jsonNode); + } // validateJson(URL) ends + + public static void validateJsonResource(String schemaResource, String jsonResource) throws IOException, + ProcessingException { + final JsonSchema schemaNode = getSchemaNode(schemaResource); + final JsonNode jsonNode = getJsonNodeFromResource(jsonResource); + validateJson(schemaNode, jsonNode); + } // validateJsonResource() ends + + private static JsonSchema _getSchemaNode(JsonNode jsonNode) throws ProcessingException { + final JsonNode schemaIdentifier = jsonNode.get(JSON_SCHEMA_IDENTIFIER_ELEMENT); + if (null == schemaIdentifier) { + ((ObjectNode) jsonNode).put(JSON_SCHEMA_IDENTIFIER_ELEMENT, JSON_V4_SCHEMA_IDENTIFIER); + } + + final JsonSchemaFactory factory = JsonSchemaFactory.byDefault(); + return factory.getJsonSchema(jsonNode); + } // _getSchemaNode() ends +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/service/VerificationService.java b/verigraph/src/main/java/it/polito/escape/verify/service/VerificationService.java new file mode 100644 index 0000000..1d31ae9 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/service/VerificationService.java @@ -0,0 +1,1185 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.service; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileWriter; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; +import javax.ws.rs.ProcessingException; +import javax.xml.bind.JAXBException; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import com.fasterxml.jackson.databind.JsonNode; + +import it.polito.escape.verify.client.Neo4jManagerClient; +import it.polito.escape.verify.exception.BadRequestException; +import it.polito.escape.verify.exception.DataNotFoundException; +import it.polito.escape.verify.exception.ForbiddenException; +import it.polito.escape.verify.exception.InternalServerErrorException; +import it.polito.escape.verify.model.Configuration; +import it.polito.escape.verify.model.Entry; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Neighbour; +import it.polito.escape.verify.model.Node; +import it.polito.escape.verify.model.Test; +import it.polito.escape.verify.model.Verification; +import it.polito.escape.verify.resources.beans.VerificationBean; +import it.polito.nffg.neo4j.jaxb.Paths; +import qj.util.ReflectUtil; +import qj.util.lang.DynamicClassLoader; + +public class VerificationService { + + private static final String generatorFolder = System.getProperty("catalina.base") + + "/webapps/verify/WEB-INF/classes/tests/j-verigraph-generator"; + + private static final String generatorFolderForGrpc = "service/src/tests/j-verigraph-generator"; + + private String testClassGenerator = generatorFolder + "/test_class_generator.py"; + + private String testGenerator = generatorFolder + "/test_generator.py"; + + public VerificationService() { + + } + + private Paths getPaths(Graph graph, Node sourceNode, Node destinationNode) { + + String source = sourceNode.getName() + "_" + sourceNode.getId(); + String destination = destinationNode.getName() + "_" + destinationNode.getId(); + + List endpoints = new ArrayList<>(); + List firewalls = new ArrayList<>(); + Map> routingTable = new HashMap<>(); + + for (Node node : graph.getNodes().values()) { + // if firewall + if (node.getFunctional_type().equals("NF")) { + // add 2 connection points to RT + routingTable.put(node.getName() + "_" + node.getId() + "_in", new ArrayList()); + routingTable.put(node.getName() + "_" + node.getId() + "_out", new ArrayList()); + // add node to firewalls + firewalls.add(node.getName() + "_" + node.getId()); + // scan neighbours + for (Neighbour neighbour : node.getNeighbours().values()) { + // check if neighbour is a firewall + Node hop = graph.searchNodeByName(neighbour.getName()); + // if neighbour is a firewall connect to its input port + if (hop.getFunctional_type().equals("NF")) + routingTable.get(node.getName() + "_" + node.getId() + "_out") + .add(new Entry("output", neighbour.getName() + "_" + hop.getId() + "_in")); + else + // connect + // normally to + // node + routingTable.get(node.getName() + "_" + node.getId() + + "_out") + .add(new Entry( "output", + neighbour.getName() + "_" + + hop.getId())); + } + } + // if endpoint + else { + // add endpoint to RT + routingTable.put(node.getName() + "_" + node.getId(), new ArrayList()); + // add to endpoints + endpoints.add(node.getName() + "_" + node.getId()); + // scan neighbours + for (Neighbour neighbour : node.getNeighbours().values()) { + // check if neighbour is a firewall + Node hop = graph.searchNodeByName(neighbour.getName()); + // if neighbour is a firewall connect to its input port + if (hop.getFunctional_type().equals("NF")) + routingTable.get(node.getName() + "_" + node.getId()) + .add(new Entry("output", neighbour.getName() + "_" + hop.getId() + "_in")); + else { + // connect + // normally to + // node + routingTable.get(node.getName() + "_" + node.getId()) + .add(new Entry("output", neighbour.getName() + "_" + hop.getId())); + } + } + } + + // end node scan + } + // debug print + System.out.println("Endpoints:"); + for (String endpoint : endpoints) { + System.out.println(endpoint); + } + System.out.println("Firewalls:"); + for (String firewall : firewalls) { + System.out.println(firewall); + } + System.out.println("Source: " + source); + System.out.println("Destination: " + destination); + for (String key : routingTable.keySet()) { + System.out.println("RT for node " + key); + for (Entry entry : routingTable.get(key)) { + System.out.println("\t" + entry.getDirection() + "->" + entry.getDestination()); + } + } + // end debug print + + Neo4jManagerClient client = new Neo4jManagerClient( "http://localhost:8080/neo4jmanager/rest/", + source, + destination, + endpoints, + firewalls, + routingTable); + + Paths paths = null; + try { + paths = client.getPaths(); + } + catch (JAXBException e) { + throw new InternalServerErrorException("Error generating input for neo4jmanager: " + e.getMessage()); + } + catch (ProcessingException e) { + throw new InternalServerErrorException("Response of neo4jmanager doesn't contain any path: " + + e.getMessage()); + } + catch (IllegalStateException e) { + throw new InternalServerErrorException("Error getting a response from neo4jmanager, no input stream for paths or input stream already consumed: " + + e.getMessage()); + } + catch (Exception e) { + throw new InternalServerErrorException("Unable to continue due to a neo4jmanager error: " + e.getMessage()); + } + + return paths; + + } + + private List sanitizePath(String path) { + List newPath = new ArrayList(); + // find all nodes, i.e. all names between parentheses + Matcher m = Pattern.compile("\\(([^)]+)\\)").matcher(path); + while (m.find()) { + String node = m.group(1); + + int spaceIndex = node.lastIndexOf("_"); + if (spaceIndex != -1) { + node = node.substring(0, spaceIndex); + newPath.add(node); + } + } + return newPath; + + } + + private List> sanitizePaths(Paths paths) { + List> sanitizedPaths = new ArrayList>(); + for (String path : paths.getPath()) { + System.out.println("Original path: " + path); + List newPath = sanitizePath(path); + sanitizedPaths.add(newPath); + } + return sanitizedPaths; + } + + static private Map toMap(List lst) { + return lst.stream().collect(Collectors.groupingBy(s -> s, Collectors.counting())); + } + + private void eliminateLoopsInPaths(List> sanitizedPaths) { + List> pathsToBeRemoved = new ArrayList>(); + + for (List path : sanitizedPaths) { + Map occurrencesMap = toMap(path); + for (long occurrences : occurrencesMap.values()) { + if (occurrences > 1) { + pathsToBeRemoved.add(path); + break; + } + } + } + for (List path : pathsToBeRemoved) { + sanitizedPaths.remove(path); + } + } + + private void printListsOfStrings(String message, List> lists) { + System.out.println(message); + for (List element : lists) { + System.out.println(element); + } + } + + private static File createTempDir(String prefix) throws IOException { + String tmpDirStr = System.getProperty("java.io.tmpdir"); + if (tmpDirStr == null) { + throw new IOException("System property 'java.io.tmpdir' does not specify a tmp dir"); + } + + File tmpDir = new File(tmpDirStr); + if (!tmpDir.exists()) { + boolean created = tmpDir.mkdirs(); + if (!created) { + throw new IOException("Unable to create tmp dir " + tmpDir); + } + } + + File resultDir = null; + int suffix = (int) System.currentTimeMillis(); + int failureCount = 0; + do { + resultDir = new File(tmpDir, prefix + suffix % 10000); + suffix++; + failureCount++; + } while (resultDir.exists() && failureCount < 50); + + if (resultDir.exists()) { + throw new IOException(failureCount + + " attempts to generate a non-existent directory name failed, giving up"); + } + boolean created = resultDir.mkdir(); + if (!created) { + throw new IOException("Failed to create tmp directory"); + } + + return resultDir; + } + + @SuppressWarnings("unchecked") + private void generateChainsFile(Graph graph, List> sanitizedPaths, String chainsFile) { + JSONObject root = new JSONObject(); + JSONArray chains = new JSONArray(); + + int chainCounter = 0; + + for (List path : sanitizedPaths) { + Iterator pathsIterator = path.iterator(); + JSONObject chain = new JSONObject(); + chain.put("id", ++chainCounter); + chain.put("flowspace", "tcp=80"); + JSONArray nodes = new JSONArray(); + while (pathsIterator.hasNext()) { + String nodeName = (String) pathsIterator.next(); + Node currentNode = graph.searchNodeByName(nodeName); + if (currentNode == null) { + throw new InternalServerErrorException("Unable to generate 'chains.json' for neo4jmanager: node " + + nodeName + " not found"); + } + JSONObject node = new JSONObject(); + node.put("name", currentNode.getName()); + // if(currentNode.getFunctional_type().equals("firewall")) + // node.put("address", "ip_nat"); + // else + node.put("address", "ip_" + currentNode.getName()); + node.put("functional_type", currentNode.getFunctional_type()); + nodes.add(node); + chain.put("nodes", nodes); + } + chains.add(chain); + } + root.put("chains", chains); + + try (FileWriter file = new FileWriter(chainsFile)) { + file.write(root.toJSONString()); + System.out.println("Successfully created 'chains.json' with the following content:"); + System.out.println(root); + } + catch (IOException e) { + throw new InternalServerErrorException("Error saving 'chains.json' for neo4jmanager"); + } + + } + + @SuppressWarnings("unchecked") + private void generateConfigFile(Graph graph, String configFile) { + JSONObject root = new JSONObject(); + JSONArray nodes = new JSONArray(); + + for (Node n : graph.getNodes().values()) { + JSONObject node = new JSONObject(); + // JSONArray configuration = new JSONArray(); + Configuration nodeConfig = n.getConfiguration(); + JsonNode configuration = nodeConfig.getConfiguration(); + + node.put("configuration", configuration); + node.put("id", nodeConfig.getId()); + node.put("description", nodeConfig.getDescription()); + + nodes.add(node); + + } + root.put("nodes", nodes); + + try (FileWriter file = new FileWriter(configFile)) { + file.write(root.toJSONString()); + System.out.println("Successfully created 'config.json' with the following content:"); + System.out.println(root); + } + catch (IOException e) { + throw new InternalServerErrorException("Error saving 'config.json' for neo4jmanager"); + } + + } + + private void printCommand(String[] cmd) { + for (String c : cmd) { + System.out.printf(c + " "); + } + System.out.println(""); + } + + private String platfromIndependentPath(String path) { + path = path.replaceAll("/", Matcher.quoteReplacement(Character.toString(File.separatorChar))); + return path; + } + + private void generateTestScenarios(String chainsFile, String configFile, String scenarioFile) { + + String[] cmd = { "python", platfromIndependentPath(testClassGenerator), "-c", + platfromIndependentPath(chainsFile), "-f", platfromIndependentPath(configFile), "-o", + platfromIndependentPath(scenarioFile) }; + printCommand(cmd); + + ProcessBuilder pb = new ProcessBuilder(cmd); + pb.redirectErrorStream(true); + Process process; + try { + process = pb.start(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = reader.readLine()) != null) + System.out.println("test_class_generator.py: " + line); + process.waitFor(); + if (process.exitValue() != 0) { + throw new InternalServerErrorException("Unable to generate test scenario file for the verification request: test_class_generator returned " + + process.exitValue()); + } + } + catch (IOException e) { + throw new InternalServerErrorException("Error generating tests for Z3: unable to execute generator"); + } + catch (InterruptedException e) { + throw new InternalServerErrorException("Error generating tests for Z3: generator got interrupted during execution"); + } + + } + + private void generateTests( int scenariosCounter, String scenariosBasename, String source, String destination, + String testsBasename) { + + List scenarios = new ArrayList(); + List tests = new ArrayList(); + for (int i = 0; i < scenariosCounter; i++) { + scenarios.add(scenariosBasename + "_" + (i + 1) + ".java"); + tests.add(testsBasename + "_" + (i + 1) + ".java"); + } + + for (int i = 0; i < scenariosCounter; i++) { + String[] cmd = { "python", platfromIndependentPath(testGenerator), "-i", + platfromIndependentPath(scenarios.get(i)), "-o", platfromIndependentPath(tests.get(i)), + "-s", source, "-d", destination }; + printCommand(cmd); + + ProcessBuilder pb = new ProcessBuilder(cmd); + pb.redirectErrorStream(true); + Process process; + try { + process = pb.start(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = reader.readLine()) != null) + System.out.println("test_generator.py: " + line); + process.waitFor(); + if (process.exitValue() != 0) { + throw new InternalServerErrorException("Unable to generate test file for the verification request: test_generator returned " + + process.exitValue()); + } + } + catch (IOException e) { + throw new InternalServerErrorException("Error generating tests for Z3: unable to execute generator"); + } + catch (InterruptedException e) { + throw new InternalServerErrorException("Error generating tests for Z3: generator got interrupted during execution"); + } + + } + + } + + private void prepareForCompilationAndExecution( int scenariosCounter, String scenarioBasename, String testBasename, + List sourceFiles, List classFiles) { + for (int i = 0; i < scenariosCounter; i++) { + String scenario = scenarioBasename + "_" + (i + 1) + ".java"; + sourceFiles.add(new File(scenario)); + System.out.println("Scenario file " + scenario + " added to compilation"); + + String testSource = testBasename + "_" + (i + 1) + ".java"; + String testClass = testBasename + "_" + (i + 1); + + sourceFiles.add(new File(testSource)); + System.out.println("Test file " + testSource + " added to copilation"); + classFiles.add(new File(testClass)); + System.out.println("Test file " + testClass + " added to execution"); + } + } + + private void compileFiles(List files, String folder) { + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + if (compiler == null) { + throw new InternalServerErrorException("Error getting the Java compiler: JDK >= 1.8 required"); + } + StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); + + try { + // fileManager.setLocation(StandardLocation.CLASS_OUTPUT, + // Arrays.asList(new File(projectFolder))); + + // String z3 = "/usr/lib/com.microsoft.z3.jar"; + // List optionList = new ArrayList(); + // optionList.add("-classpath"); + // optionList.add(System.getProperty("java.class.path") + ":" + z3); + List optionList = new ArrayList(); + optionList.add("-d"); + optionList.add(folder); + DiagnosticCollector diagnostics = new DiagnosticCollector<>(); + + boolean success = compiler + .getTask( null, + fileManager, + diagnostics, + optionList, + null, + fileManager.getJavaFileObjectsFromFiles(files)) + .call(); + if (!success) { + Locale myLocale = Locale.getDefault(); + StringBuilder msg = new StringBuilder(); + msg.append("Error compiling Z3 test files: "); + for (Diagnostic err : diagnostics.getDiagnostics()) { + msg.append('\n'); + msg.append(err.getKind()); + msg.append(": "); + if (err.getSource() != null) { + msg.append(err.getSource().getName()); + } + msg.append(':'); + msg.append(err.getLineNumber()); + msg.append(": "); + msg.append(err.getMessage(myLocale)); + } + throw new InternalServerErrorException(msg.toString()); + } + fileManager.close(); + + } + catch (IOException e) { + throw new InternalServerErrorException("Unable to set the location of the Z3 test files to be compiled"); + } + + } + + private int runIt(File filename, String folder) { + int endIndex = filename.getName().lastIndexOf("."); + String filenameNoExtension; + if (endIndex == -1) { + filenameNoExtension = filename.getName(); + } + else { + filenameNoExtension = filename.getName().substring(0, endIndex); + if (!filenameNoExtension.matches("\\w+")) { + filenameNoExtension = filename.getName(); + } + } + + System.out.println("Filename is: " + filenameNoExtension); + try { + Class userClass = new DynamicClassLoader(folder).load("tests." + filenameNoExtension); + Object context = ReflectUtil.newInstance(userClass); + Object result = ReflectUtil.invoke("run", context); + return (int) result; + } + catch (Exception e) { + StringWriter errors = new StringWriter(); + e.printStackTrace(new PrintWriter(errors)); + throw new InternalServerErrorException("Error executing Z3 tests: " + e.getMessage() + + ". There are errors in the Z3 model."); + } + } + + private List runFiles(String folder, List> paths, Graph graph, List files) { + List tests = new ArrayList(); + for (int i = 0; i < files.size(); i++) { + System.out.println("Running test file \"" + files.get(i).getAbsolutePath() + "\""); + int result = runIt(files.get(i), folder); + System.out.println("Execution returned: " + result); + + List path = new ArrayList(); + for (String nodeString : paths.get(i)) { + Node node = graph.searchNodeByName(nodeString); + path.add(node); + } + Test t = new Test(path, result); + tests.add(t); + } + + return tests; + } + + @SuppressWarnings("unused") + private static boolean deleteDir(File dir) { + if (dir.isDirectory()) { + String[] children = dir.list(); + for (int i = 0; i < children.length; i++) { + boolean success = deleteDir(new File(dir, children[i])); + if (!success) { + return false; + } + } + } + + return dir.delete(); + + } + + @SuppressWarnings("unused") + private void deleteFilesWithPrefix(String directory, String prefix, String extension) { + final File scenarioFolder = new File(directory); + final File[] scenarioFiles = scenarioFolder.listFiles(new FilenameFilter() { + + @Override + public boolean accept(final File dir, final String name) { + return name.matches(prefix + ".*\\." + extension); + } + }); + for (final File file : scenarioFiles) { + if (!file.delete()) { + System.err.println("Can't remove " + file.getAbsolutePath()); + } + else { + System.out.println("Removed file " + file.getAbsolutePath()); + } + } + } + + public Verification verify(long graphId, VerificationBean verificationBean) { + if (graphId <= 0) { + throw new ForbiddenException("Illegal graph id: " + graphId); + } + GraphService graphService = new GraphService(); + Graph graph = graphService.getGraph(graphId); + if (graph == null) { + throw new DataNotFoundException("Graph with id " + graphId + " not found"); + } + String source = verificationBean.getSource(); + String destination = verificationBean.getDestination(); + String type = verificationBean.getType(); + if (source == null || source.equals("")) { + throw new BadRequestException("Please specify the 'source' parameter in your request"); + } + if (destination == null || destination.equals("")) { + throw new BadRequestException("Please specify the 'destination' parameter in your request"); + } + if (type == null || type.equals("")) { + throw new BadRequestException("Please specify the 'type' parameter in your request"); + } + + Node sourceNode = graph.searchNodeByName(verificationBean.getSource()); + Node destinationNode = graph.searchNodeByName(verificationBean.getDestination()); + + if (sourceNode == null) { + throw new BadRequestException("The 'source' parameter '" + source + "' is not valid, please insert the name of an existing node"); + } + if (destinationNode == null) { + throw new BadRequestException("The 'destination' parameter '" + destination + "' is not valid, please insert the name of an existing node"); + } + if ((!type.equals("reachability")) && (!type.equals("isolation")) && (!type.equals("traversal"))) { + throw new BadRequestException("The 'type' parameter '" + type + + "' is not valid: valid types are: 'reachability', 'isolation' and 'traversal'"); + } + + Verification v = null; + String middlebox; + Node middleboxNode; + switch (type) { + case "reachability": + v = reachabilityVerification(graph, sourceNode, destinationNode); + break; + case "isolation": + middlebox = verificationBean.getMiddlebox(); + if (middlebox == null || middlebox.equals("")) { + throw new BadRequestException("Please specify the 'middlebox' parameter in your request"); + } + + middleboxNode = graph.searchNodeByName(middlebox); + if (middleboxNode == null) { + throw new BadRequestException("The 'middlebox' parameter '" + middlebox + "' is not valid, please insert the name of an existing node"); + } + if (middleboxNode.getFunctional_type().equals("endpoint")) { + throw new BadRequestException("'" + middlebox + + "' is of type 'endpoint', please choose a valid middlebox"); + } + v = isolationVerification(graph, sourceNode, destinationNode, middleboxNode); + break; + case "traversal": + middlebox = verificationBean.getMiddlebox(); + if (middlebox == null || middlebox.equals("")) { + throw new BadRequestException("Please specify the 'middlebox' parameter in your request"); + } + + middleboxNode = graph.searchNodeByName(middlebox); + if (middleboxNode == null) { + throw new BadRequestException("The 'middlebox' parameter '" + middlebox + "' is not valid, please insert the name of an existing node"); + } + if (middleboxNode.getFunctional_type().equals("endpoint")) { + throw new BadRequestException("'" + middlebox + + "' is of type 'endpoint', please choose a valid middlebox"); + } + v = traversalVerification(graph, sourceNode, destinationNode, middleboxNode); + break; + default: + break; + } + + return v; + } + + private Verification isolationVerification(Graph graph, Node sourceNode, Node destinationNode, Node middleboxNode) { + + Paths paths = getPaths(graph, sourceNode, destinationNode); + if (paths.getPath().size() == 0) { + return new Verification("UNSAT", + "There are no available paths between '" + sourceNode.getName() + "' and '" + + destinationNode.getName() + "'"); + } + + List> sanitizedPaths = sanitizePaths(paths); + + printListsOfStrings("Before loops removal", sanitizedPaths); + + eliminateLoopsInPaths(sanitizedPaths); + + printListsOfStrings("After loops removal", sanitizedPaths); + + if (sanitizedPaths.isEmpty()) { + return new Verification("UNSAT", + "There are no available paths between '" + sourceNode.getName() + "' and '" + + destinationNode.getName() + "'"); + } + + List tests = extractTestsFromPaths(graph, sanitizedPaths, "UNKNWON"); + + extractPathsWithMiddlebox(sanitizedPaths, middleboxNode.getName()); + + if (sanitizedPaths.isEmpty()) { + return new Verification("UNSAT", + tests, + "There are no available paths between '" + sourceNode.getName() + "' and '" + + destinationNode.getName() + "' which traverse middlebox '" + + middleboxNode.getName() + "'. See below all the available paths."); + } + + printListsOfStrings("Paths with middlebox '" + middleboxNode.getName() + "'", sanitizedPaths); + + File tempDir = null; + + try { + tempDir = createTempDir("isolation"); + } + catch (IOException e) { + throw new InternalServerErrorException("Unable to perform verification: " + e.getMessage()); + } + + String chainsFile = tempDir.getAbsolutePath() + "/chains.json"; + generateChainsFile(graph, sanitizedPaths, chainsFile); + + String configFile = tempDir.getAbsolutePath() + "/config.json"; + generateConfigFile(graph, configFile); + + String isolationScenariosBasename = tempDir.getAbsolutePath() + "/IsolationScenario"; + try{ + generateTestScenarios(chainsFile, configFile, isolationScenariosBasename); + }catch(Exception ex){ + testClassGenerator = generatorFolderForGrpc + "/test_class_generator.py"; + generateTestScenarios(chainsFile, configFile, isolationScenariosBasename); + } + + String isolationTestsBasename = tempDir.getAbsolutePath() + "/IsolationTest"; + try{ + generateTests( sanitizedPaths.size(), + isolationScenariosBasename, + sourceNode.getName(), + middleboxNode.getName(), + isolationTestsBasename); + }catch(InternalServerErrorException ex){ + testGenerator = generatorFolderForGrpc + "/test_generator.py"; + generateTests( sanitizedPaths.size(), + isolationScenariosBasename, + sourceNode.getName(), + middleboxNode.getName(), + isolationTestsBasename); + } + + List sourceFiles = new ArrayList(); + List classFiles = new ArrayList(); + prepareForCompilationAndExecution( sanitizedPaths.size(), + isolationScenariosBasename, + isolationTestsBasename, + sourceFiles, + classFiles); + + compileFiles(sourceFiles, tempDir.getAbsolutePath()); + + tests = runFiles(tempDir.getAbsolutePath(), sanitizedPaths, graph, classFiles); + + return evaluateIsolationResults(tests, + sourceNode.getName(), + destinationNode.getName(), + middleboxNode.getName()); + + } + + private List extractTestsFromPaths(Graph graph, List> paths, String result) { + List tests = new ArrayList(); + for (List path : paths) { + List nodes = new ArrayList(); + for (String nodeName : path) { + nodes.add(graph.searchNodeByName(nodeName)); + } + tests.add(new Test(nodes, result)); + } + return tests; + } + + private Verification evaluateIsolationResults( List tests, String source, String destination, + String middlebox) { + Verification v = new Verification(); + boolean isSat = false; + int unsatCounter = 0; + for (Test t : tests) { + v.getTests().add(t); + + if (t.getResult().equals("SAT")) { + isSat = true; + } + else if (t.getResult().equals("UNKNOWN")) { + v.setResult("UNKNWON"); + v.setComment("Isolation property with source '" + source + "', destination '" + destination + + "' and middlebox '" + middlebox + "' is UNKNOWN because although '" + source + + "' cannot reach '" + middlebox + "' in any path from '" + source + "' to '" + + destination + "' which traverses middlebox '" + middlebox + + "' at least one reachability test between '" + source + "' and '" + middlebox + + "' returned UNKNOWN (see below all the paths that have been checked)"); + } + else if (t.getResult().equals("UNSAT")) { + unsatCounter++; + } + } + if (isSat) { + v.setResult("UNSAT"); + v.setComment("Isolation property with source '" + source + "', destination '" + destination + + "' and middlebox '" + middlebox + "' is UNSATISFIED because reachability between '" + + source + "' and '" + middlebox + "' is SATISFIED in at least one path between '" + source + + "' and '" + destination + "' which traverses middlebox '" + middlebox + + "' (see below all the paths that have been checked)"); + } + else if (unsatCounter == tests.size()) { + v.setResult("SAT"); + v.setComment("Isolation property with source '" + source + "', destination '" + destination + + "' and middlebox '" + middlebox + "' is SATISFIED because reachability between '" + source + + "' and '" + middlebox + "' is UNSATISFIED in all paths between '" + source + "' and '" + + destination + "' which traverse middlebox '" + middlebox + + "' (see below all the paths that have been checked)"); + } + return v; + + } + + private void extractPathsWithMiddlebox(List> sanitizedPaths, String middleboxName) { + List> pathsToBeRemoved = new ArrayList>(); + for (List path : sanitizedPaths) { + boolean middleboxFound = false; + for (String node : path) { + if (node.equals(middleboxName)) { + middleboxFound = true; + break; + } + } + if (!middleboxFound) { + pathsToBeRemoved.add(path); + } + } + + for (List path : pathsToBeRemoved) { + sanitizedPaths.remove(path); + } + + } + + private void extractPathsWithoutMiddlebox(List> sanitizedPaths, String middleboxName) { + List> pathsToBeRemoved = new ArrayList>(); + for (List path : sanitizedPaths) { + boolean middleboxFound = false; + for (String node : path) { + if (node.equals(middleboxName)) { + middleboxFound = true; + break; + } + } + if (middleboxFound) { + pathsToBeRemoved.add(path); + } + } + + for (List path : pathsToBeRemoved) { + sanitizedPaths.remove(path); + } + + } + + private Verification traversalVerification(Graph graph, Node sourceNode, Node destinationNode, Node middleboxNode) { + + Paths paths = getPaths(graph, sourceNode, destinationNode); + if (paths.getPath().size() == 0) { + return new Verification("UNSAT", + "There are no available paths between '" + sourceNode.getName() + "' and '" + + destinationNode.getName() + "'"); + } + + List> pathsBetweenSourceAndDestination = sanitizePaths(paths); + + printListsOfStrings("Before loops removal", pathsBetweenSourceAndDestination); + + eliminateLoopsInPaths(pathsBetweenSourceAndDestination); + + printListsOfStrings("After loops removal", pathsBetweenSourceAndDestination); + + if (pathsBetweenSourceAndDestination.isEmpty()) { + return new Verification("UNSAT", + "There are no available paths between '" + sourceNode.getName() + "' and '" + + destinationNode.getName() + "'"); + } + + List tests = extractTestsFromPaths(graph, pathsBetweenSourceAndDestination, "UNKNOWN"); + + List> pathsWithMiddlebox = new ArrayList>(); + for (List path : pathsBetweenSourceAndDestination) { + pathsWithMiddlebox.add(path); + } + + extractPathsWithMiddlebox(pathsWithMiddlebox, middleboxNode.getName()); + + if (pathsWithMiddlebox.isEmpty()) { + return new Verification("UNSAT", + tests, + "There are no paths between '" + sourceNode.getName() + "' and '" + + destinationNode.getName() + "' which traverse middlebox '" + + middleboxNode.getName() + "'. See below all the available paths"); + } + + printListsOfStrings("Paths with middlebox '" + middleboxNode.getName() + "'", pathsWithMiddlebox); + + File tempDir = null; + + try { + tempDir = createTempDir("traversal"); + } + catch (IOException e) { + throw new InternalServerErrorException("Unable to perform verification: " + e.getMessage()); + } + + String chainsFile = tempDir.getAbsolutePath() + "/chains.json"; + generateChainsFile(graph, pathsWithMiddlebox, chainsFile); + + String configFile = tempDir.getAbsolutePath() + "/config.json"; + generateConfigFile(graph, configFile); + + String traversalScenariosBasename = tempDir.getAbsolutePath() + "/TraversalScenario"; + try{ + generateTestScenarios(chainsFile, configFile, traversalScenariosBasename); + }catch(Exception ex){ + testClassGenerator = generatorFolderForGrpc + "/test_class_generator.py"; + generateTestScenarios(chainsFile, configFile, traversalScenariosBasename); + } + + String traversalTestsBasename = tempDir.getAbsolutePath() + "/TraversalTest"; + try{ + generateTests( pathsWithMiddlebox.size(), + traversalScenariosBasename, + sourceNode.getName(), + destinationNode.getName(), + traversalTestsBasename); + }catch(InternalServerErrorException ex){ + testGenerator = generatorFolderForGrpc + "/test_generator.py"; + generateTests( pathsWithMiddlebox.size(), + traversalScenariosBasename, + sourceNode.getName(), + destinationNode.getName(), + traversalTestsBasename); + } + List sourceFiles = new ArrayList(); + List classFiles = new ArrayList(); + prepareForCompilationAndExecution( pathsWithMiddlebox.size(), + traversalScenariosBasename, + traversalTestsBasename, + sourceFiles, + classFiles); + + compileFiles(sourceFiles, tempDir.getAbsolutePath()); + + tests = runFiles(tempDir.getAbsolutePath(), pathsWithMiddlebox, graph, classFiles); + + for (Test t : tests) { + if (t.getResult().equals("UNSAT")) { + return new Verification("UNSAT", + tests, + "There is at least a path between '" + sourceNode.getName() + "' and '" + + destinationNode.getName() + "' traversing middlebox '" + + middleboxNode.getName() + "' where '" + sourceNode.getName() + + "' cannot reach '" + destinationNode.getName() + + "'. See below the paths that have been checked"); + } + if (t.getResult().equals("UNKNOWN")) { + return new Verification("UNKNOWN", + tests, + "There is at least a path between '" + sourceNode.getName() + "' and '" + + destinationNode.getName() + "' traversing middlebox '" + + middleboxNode.getName() + "' where it is not guaranteed that '" + + sourceNode.getName() + "' can effectively reach '" + + destinationNode.getName() + + "'. See below the paths that have been checked"); + } + } + + extractPathsWithoutMiddlebox(pathsBetweenSourceAndDestination, middleboxNode.getName()); + printListsOfStrings("Paths without middlebox '" + middleboxNode.getName() + "'", pathsBetweenSourceAndDestination); + + if (pathsBetweenSourceAndDestination.isEmpty()) { + return new Verification("SAT", + tests, + "All the paths between node '" + sourceNode.getName() + "' and '" + + destinationNode.getName() + "' traverse middlebox '" + + middleboxNode.getName() + "'"); + } + + tempDir = null; + + try { + tempDir = createTempDir("traversal"); + } + catch (IOException e) { + throw new InternalServerErrorException("Unable to perform verification: " + e.getMessage()); + } + + chainsFile = tempDir.getAbsolutePath() + "/chains.json"; + generateChainsFile(graph, pathsBetweenSourceAndDestination, chainsFile); + + configFile = tempDir.getAbsolutePath() + "/config.json"; + generateConfigFile(graph, configFile); + + traversalScenariosBasename = tempDir.getAbsolutePath() + "/TraversalScenario"; + generateTestScenarios(chainsFile, configFile, traversalScenariosBasename); + + traversalTestsBasename = tempDir.getAbsolutePath() + "/TraversalTest"; + generateTests( pathsBetweenSourceAndDestination.size(), + traversalScenariosBasename, + sourceNode.getName(), + destinationNode.getName(), + traversalTestsBasename); + + sourceFiles = new ArrayList(); + classFiles = new ArrayList(); + prepareForCompilationAndExecution( pathsBetweenSourceAndDestination.size(), + traversalScenariosBasename, + traversalTestsBasename, + sourceFiles, + classFiles); + + compileFiles(sourceFiles, tempDir.getAbsolutePath()); + + tests = runFiles(tempDir.getAbsolutePath(), pathsBetweenSourceAndDestination, graph, classFiles); + + return evaluateTraversalResults(tests, + sourceNode.getName(), + destinationNode.getName(), + middleboxNode.getName()); + + } + + private Verification evaluateTraversalResults( List tests, String source, String destination, + String middlebox) { + Verification v = new Verification(); + boolean isSat = false; + int unsatCounter = 0; + for (Test t : tests) { + v.getTests().add(t); + + if (t.getResult().equals("SAT")) { + isSat = true; + } + else if (t.getResult().equals("UNKNOWN")) { + v.setResult("UNKNWON"); + v.setComment("There is at least one path from '" + source + "' to '" + destination + + "' that doesn't traverse middlebox '" + middlebox + + "' (see below all the paths that have been checked)"); + } + else if (t.getResult().equals("UNSAT")) { + unsatCounter++; + } + } + if (isSat) { + v.setResult("UNSAT"); + v.setComment("There is at least one path from '" + source + "' to '" + destination + + "' that doesn't traverse middlebox '" + middlebox + + "' (see below all the paths that have been checked)"); + } + else if (unsatCounter == tests.size()) { + v.setResult("SAT"); + v.setComment("The only available paths from '" + source + "' to '" + destination + + "' are those that traverse middlebox '" + middlebox + + "' (see below the alternative paths that have been checked and are unusable)"); + } + return v; + } + + private Verification reachabilityVerification(Graph graph, Node sourceNode, Node destinationNode) { + Paths paths = getPaths(graph, sourceNode, destinationNode); + + if (paths.getPath().size() == 0) { + return new Verification("UNSAT", + "There are no available paths between '" + sourceNode.getName() + "' and '" + + destinationNode.getName() + "'"); + } + + List> sanitizedPaths = sanitizePaths(paths); + + printListsOfStrings("Before loops removal", sanitizedPaths); + + eliminateLoopsInPaths(sanitizedPaths); + + printListsOfStrings("After loops removal", sanitizedPaths); + + if (sanitizedPaths.isEmpty()) { + return new Verification("UNSAT", + "There are no available paths between '" + sourceNode.getName() + "' and '" + + destinationNode.getName() + "'"); + } + + File tempDir = null; + + try { + tempDir = createTempDir("reachability"); + } + catch (IOException e) { + throw new InternalServerErrorException("Unable to perform verification: " + e.getMessage()); + } + + String chainsFile = tempDir.getAbsolutePath() + "/chains.json"; + generateChainsFile(graph, sanitizedPaths, chainsFile); + + String configFile = tempDir.getAbsolutePath() + "/config.json"; + generateConfigFile(graph, configFile); + + String reachabilityScenariosBasename = tempDir.getAbsolutePath() + "/ReachabilityScenario"; + try{ + generateTestScenarios(chainsFile, configFile, reachabilityScenariosBasename); + }catch(InternalServerErrorException ex){ + testClassGenerator = generatorFolderForGrpc + "/test_class_generator.py"; + generateTestScenarios(chainsFile, configFile, reachabilityScenariosBasename); + } + + + String reachabilityTestsBasename = tempDir.getAbsolutePath() + "/ReachabilityTest"; + try{ + generateTests( sanitizedPaths.size(), + reachabilityScenariosBasename, + sourceNode.getName(), + destinationNode.getName(), + reachabilityTestsBasename); + }catch(InternalServerErrorException ex){ + testGenerator = generatorFolderForGrpc + "/test_generator.py"; + generateTests( sanitizedPaths.size(), + reachabilityScenariosBasename, + sourceNode.getName(), + destinationNode.getName(), + reachabilityTestsBasename); + } + List sourceFiles = new ArrayList(); + List classFiles = new ArrayList(); + prepareForCompilationAndExecution( sanitizedPaths.size(), + reachabilityScenariosBasename, + reachabilityTestsBasename, + sourceFiles, + classFiles); + + compileFiles(sourceFiles, tempDir.getAbsolutePath()); + + List tests = runFiles(tempDir.getAbsolutePath(), sanitizedPaths, graph, classFiles); + + return evaluateReachabilityResult(tests, sourceNode.getName(), destinationNode.getName()); + } + + private Verification evaluateReachabilityResult(List tests, String source, String destination) { + Verification v = new Verification(); + boolean sat = false; + int unsat = 0; + for (Test t : tests) { + v.getTests().add(t); + + if (t.getResult().equals("SAT")) { + sat = true; + } + else if (t.getResult().equals("UNKNOWN")) { + v.setResult("UNKNWON"); + v.setComment("Reachability from '" + source + "' to '" + destination + + "' is unknown. See all the checked paths below"); + } + else if (t.getResult().equals("UNSAT")) { + unsat++; + } + } + if (sat) { + v.setResult("SAT"); + v.setComment("There is at least one path '" + source + "' can use to reach '" + destination + + "'. See all the available paths below"); + } + else if (unsat == tests.size()) { + v.setResult("UNSAT"); + v.setComment("There isn't any path '" + source + "' can use to reach '" + destination + + "'. See all the checked paths below"); + } + return v; + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/test/MultiThreadedTestCase.java b/verigraph/src/main/java/it/polito/escape/verify/test/MultiThreadedTestCase.java new file mode 100644 index 0000000..7989b37 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/test/MultiThreadedTestCase.java @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import javax.ws.rs.core.Response; + +import org.junit.Assert; +import org.junit.Test; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import it.polito.escape.verify.client.VerifyClient; +import it.polito.escape.verify.client.VerifyClientException; +import it.polito.escape.verify.model.Configuration; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Node; + +public class MultiThreadedTestCase { + + private void testUpdateGraphStatus(final int threadCount) throws InterruptedException, ExecutionException, JsonParseException, JsonMappingException, IOException, VerifyClientException { + final VerifyClient verifyClient = new VerifyClient("http://localhost:8080/verify/api"); + + Response retrieveGraphResponse = verifyClient.retrieveGraph(1); + String responseString = retrieveGraphResponse.readEntity(String.class); + System.out.println(responseString); + + Graph graph = new ObjectMapper().readValue(responseString, Graph.class); + + UpdateGraph task = new UpdateGraph(verifyClient, 1, graph); + + List tasks = Collections.nCopies(threadCount, task); + ExecutorService executorService = Executors.newFixedThreadPool(threadCount); + + List> futures = executorService.invokeAll(tasks); + List resultList = new ArrayList(futures.size()); + + // Check for exceptions + for (Future future : futures) { + // Throws an exception if an exception was thrown by the task. + resultList.add(future.get().getStatus()); + } + // Validate the dimensions + Assert.assertEquals(threadCount, futures.size()); + List expectedList = new ArrayList(threadCount); + for (int i = 1; i <= threadCount; i++) { + expectedList.add(200); + } + // Validate expected results + Assert.assertEquals(expectedList, resultList); + } + + private void testUpdateGraph(final int threadCount) throws InterruptedException, ExecutionException, JsonParseException, JsonMappingException, IOException, VerifyClientException { + final VerifyClient verifyClient = new VerifyClient("http://localhost:8080/verify/api"); + + Response retrieveGraphResponse = verifyClient.retrieveGraph(2L); + String responseString = retrieveGraphResponse.readEntity(String.class); + System.out.println(responseString); + + Graph graph = new ObjectMapper().readValue(responseString, Graph.class); + + Node nodeToEdit = graph.getNodes().get(1L); + nodeToEdit.setFunctional_type("endpoint"); + nodeToEdit.setConfiguration(new Configuration(nodeToEdit.getName(), "", new ObjectMapper().createArrayNode())); + + String graphAsString = new ObjectMapper().writeValueAsString(graph); + System.out.println(graphAsString); + + UpdateGraph task = new UpdateGraph(verifyClient, 2, graph); + + List tasks = Collections.nCopies(threadCount, task); + ExecutorService executorService = Executors.newFixedThreadPool(threadCount); + + List> futures = executorService.invokeAll(tasks); + List resultList = new ArrayList(futures.size()); + + // Check for exceptions + for (Future future : futures) { + // Throws an exception if an exception was thrown by the task. + resultList.add(future.get().readEntity(String.class)); + } + // Validate dimensions + Assert.assertEquals(threadCount, futures.size()); + + List expectedList = new ArrayList(threadCount); + for (int i = 1; i <= threadCount; i++) { + expectedList.add(graphAsString); + } + // Validate expected results + Assert.assertEquals(expectedList, resultList); + } + + private void testCreateGraphStatus(final int threadCount, Graph graph) throws InterruptedException, ExecutionException, JsonParseException, JsonMappingException, IOException { + final VerifyClient verifyClient = new VerifyClient("http://localhost:8080/verify/api"); + + CreateGraph task = new CreateGraph(verifyClient, graph); + + List tasks = Collections.nCopies(threadCount, task); + ExecutorService executorService = Executors.newFixedThreadPool(threadCount); + + List> futures = executorService.invokeAll(tasks); + List resultList = new ArrayList(futures.size()); + + // Check for exceptions + for (Future future : futures) { + // Throws an exception if an exception was thrown by the task. + resultList.add(future.get().getStatus()); + } + // Validate the IDs + Assert.assertEquals(threadCount, futures.size()); + + List expectedList = new ArrayList(threadCount); + for (int i = 1; i <= threadCount; i++) { + expectedList.add(201); + } + // Validate expected results + Assert.assertEquals(expectedList, resultList); + } + + private int randInt(int min, int max){ + Random rand = new Random(); + int randomNum = rand.nextInt((max - min) + 1) + min; + return randomNum; + } + + @Test + public void updateGraphStatusCheck() throws InterruptedException, ExecutionException, JsonParseException, JsonMappingException, IOException, VerifyClientException { + testUpdateGraphStatus(64); + } + + @Test + public void updateGraphResponseCheck() throws InterruptedException, ExecutionException, JsonParseException, JsonMappingException, IOException, VerifyClientException { + testUpdateGraph(16); + } + + @Test + public void createGraphStatusCheck() throws JsonParseException, JsonMappingException, InterruptedException, ExecutionException, IOException { + Graph graph = new Graph(); + testCreateGraphStatus(8, graph); + } + + class UpdateGraph implements Callable { + + private VerifyClient verifyClient; + + private int graphId; + + private Graph graph; + + public UpdateGraph(VerifyClient verifyClient, int graphId, Graph graph) { + this.graphId = graphId; + this.graph = graph; + this.verifyClient = verifyClient; + } + + @Override + public Response call() throws Exception { + Thread.sleep(randInt(0,2000)); + Response updateGraphResponse = this.verifyClient.updateGraph(this.graphId, this.graph); + return updateGraphResponse; + } + } + + class CreateGraph implements Callable { + + private VerifyClient verifyClient; + + private Graph graph; + + public CreateGraph(VerifyClient verifyClient, Graph graph) { + this.graph = graph; + this.verifyClient = verifyClient; + } + + @Override + public Response call() throws Exception { + Thread.sleep(randInt(0,2000)); + return this.verifyClient.createGraph(this.graph); + } + + } +} \ No newline at end of file diff --git a/verigraph/src/main/java/it/polito/escape/verify/test/Scalability.java b/verigraph/src/main/java/it/polito/escape/verify/test/Scalability.java new file mode 100644 index 0000000..3039ca0 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/test/Scalability.java @@ -0,0 +1,409 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.test; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import it.polito.escape.verify.client.VerifyClient; +import it.polito.escape.verify.client.VerifyClientException; +import it.polito.escape.verify.model.Configuration; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Neighbour; +import it.polito.escape.verify.model.Node; +import it.polito.escape.verify.model.Verification; + +public class Scalability { + + private VerifyClient client = new VerifyClient("http://localhost:8080/verify/api"); + + public static void main(String[] args) throws VerifyClientException { + Scalability s = new Scalability(); + + reachabilityTest(s, 10); + + isolationTest(s,1500); + traversalTest(s,600); + } + + private static void reachabilityTest(Scalability s, int n) throws VerifyClientException { + System.out.printf("Reachability test with N=" + n + ": "); + printTimestamp(); + Graph graph = generateNatScenario(n); + Graph createdGraph = s.client.createGraph(graph).readEntity(Graph.class); + Verification result = s.client.getReachability(createdGraph.getId(), "client", "server"); + System.out.println("Test returned " + result.getResult()); + System.out.printf("Finished reachability test with N=" + n + ": "); + printTimestamp(); + System.out.println(); + } + + private static void isolationTest(Scalability s, int n) throws VerifyClientException { + System.out.printf("Isolation test with N=" + n + ": "); + printTimestamp(); + Graph graph = generateNatScenario(n); + Graph createdGraph = s.client.createGraph(graph).readEntity(Graph.class); + Verification result = s.client.getIsolation(createdGraph.getId(), "client", "server", "firewall"); + System.out.println("Test returned " + result.getResult()); + System.out.printf("Finished isolation test with N=" + n + ": "); + printTimestamp(); + System.out.println(); + } + + private static void traversalTest(Scalability s, int n) throws VerifyClientException { + System.out.printf("Traversal test with N=" + n + ": "); + printTimestamp(); + Graph graph = generateNatScenario(n); + Graph createdGraph = s.client.createGraph(graph).readEntity(Graph.class); + Verification result = s.client.getTraversal(createdGraph.getId(), "client", "server", "firewall"); + System.out.println("Test returned " + result.getResult()); +// System.out.println("Result explanation: " + result.getComment()); + System.out.printf("Finished traversal test with N=" + n + ": "); + printTimestamp(); + System.out.println(); + } + + private static void printTimestamp() { + java.util.Date date= new java.util.Date(); + System.out.println(new Timestamp(date.getTime())); + } + + private static Graph generateNatScenario(int n) { + List nodes = new ArrayList(); + + Node client = new Node(); + client.setName("client"); + client.setFunctional_type("endhost"); + ArrayNode clientConfigArray = new ObjectMapper().createArrayNode(); + JsonNode clientConfig = new ObjectMapper().createObjectNode(); + ((ObjectNode)clientConfig).put("url", "www.facebook.com"); + ((ObjectNode)clientConfig).put("body", "word"); + ((ObjectNode)clientConfig).put("destination","server"); + ((ObjectNode)clientConfig).put("protocol", "HTTP_REQUEST"); + clientConfigArray.add(clientConfig); + client.setConfiguration(new Configuration(client.getName(),"", clientConfigArray)); + + Map clientNeighbours = new HashMap(); + clientNeighbours.put(1L, new Neighbour(1L, "nat1")); + client.setNeighbours(clientNeighbours ); + //add client to list + nodes.add(client); + + for(int i=0; i< n;i++){ + Node nat = new Node(); + nat.setId(i+1); + nat.setName("nat" + (i+1)); + nat.setFunctional_type("nat"); + ArrayNode configArray = new ObjectMapper().createArrayNode(); + + Map natNeighbours = new HashMap(); + + //set left neighbour for each node except the first + if(nat.getId() != 1){ + natNeighbours.put(1L, new Neighbour(1L, "nat" + i)); + configArray.add("client"); + for (int j=1; j <= i; j++){ + configArray.add("nat" + j); + } + } + //first nat: set only client as neighbour and natted node + else{ + natNeighbours.put(1L, new Neighbour(1L, "client")); + configArray.add("client"); + } + //set right neighbour for each node except the last + if(nat.getId() != n){ + natNeighbours.put(2L, new Neighbour(1L, "nat" + (i+2))); + } + //last nat: set server as neighbour + else{ + natNeighbours.put(2L, new Neighbour(1L, "server")); + } + + nat.setNeighbours(natNeighbours); + nat.setConfiguration(new Configuration(nat.getName(),"", configArray)); + + //add nat to list + nodes.add(nat); + } + + Node server = new Node(); + server.setName("server"); + server.setFunctional_type("webserver"); + ArrayNode serverConfigArray = new ObjectMapper().createArrayNode(); + server.setConfiguration(new Configuration(server.getName(),"", serverConfigArray)); + + Map serverNeighbours = new HashMap(); + serverNeighbours.put(1L, new Neighbour(1L, "nat" + (n))); + server.setNeighbours(serverNeighbours ); + + //add server to list + nodes.add(server); + + //create graph + Graph g = new Graph(); + Map graphNodes = new HashMap(); + long index = 1L; + for (Node node : nodes){ + graphNodes.put(index, node); + index++; + } + g.setNodes(graphNodes); + + return g; + } + + private static Graph generateFirewallScenario(int n) { + List nodes = new ArrayList(); + + Node client = new Node(); + client.setName("client"); + client.setFunctional_type("endhost"); + ArrayNode clientConfigArray = new ObjectMapper().createArrayNode(); + JsonNode clientConfig = new ObjectMapper().createObjectNode(); + ((ObjectNode)clientConfig).put("url", "www.facebook.com"); + ((ObjectNode)clientConfig).put("body", "word"); + ((ObjectNode)clientConfig).put("destination","server"); + ((ObjectNode)clientConfig).put("protocol", "HTTP_REQUEST"); + clientConfigArray.add(clientConfig); + client.setConfiguration(new Configuration(client.getName(),"", clientConfigArray)); + + Map clientNeighbours = new HashMap(); + clientNeighbours.put(1L, new Neighbour(1L, "firewall1")); + client.setNeighbours(clientNeighbours ); + //add client to list + nodes.add(client); + + for(int i=0; i< n;i++){ + Node firewall = new Node(); + firewall.setId(i+1); + firewall.setName("firewall" + (i+1)); + firewall.setFunctional_type("firewall"); + ArrayNode configArray = new ObjectMapper().createArrayNode(); + + Map natNeighbours = new HashMap(); + + //set left neighbour for each node except the first + if(firewall.getId() != 1){ + natNeighbours.put(1L, new Neighbour(1L, "firewall" + i)); + } + //first firewall: set only client as neighbour and natted node + else{ + natNeighbours.put(1L, new Neighbour(1L, "client")); + } + //set right neighbour for each node except the last + if(firewall.getId() != n){ + natNeighbours.put(2L, new Neighbour(1L, "firewall" + (i+2))); + } + //last firewall: set server as neighbour + else{ + natNeighbours.put(2L, new Neighbour(1L, "server")); + } + + firewall.setNeighbours(natNeighbours); + firewall.setConfiguration(new Configuration(firewall.getName(),"", configArray)); + + //add nat to list + nodes.add(firewall); + } + + Node server = new Node(); + server.setName("server"); + server.setFunctional_type("webserver"); + ArrayNode serverConfigArray = new ObjectMapper().createArrayNode(); + server.setConfiguration(new Configuration(server.getName(),"", serverConfigArray)); + + Map serverNeighbours = new HashMap(); + serverNeighbours.put(1L, new Neighbour(1L, "firewall" + (n))); + server.setNeighbours(serverNeighbours ); + + //add server to list + nodes.add(server); + + //create graph + Graph g = new Graph(); + Map graphNodes = new HashMap(); + long index = 1L; + for (Node node : nodes){ + graphNodes.put(index, node); + index++; + } + g.setNodes(graphNodes); + + return g; + } + + private static Graph generateScenario(int n) { + List nodes = new ArrayList(); + + Node firewall = new Node(); + firewall.setName("firewall"); + firewall.setFunctional_type("firewall"); + ArrayNode firewallConfigArray = new ObjectMapper().createArrayNode(); + Map firewallNeighbours = new HashMap(); + + for (int i=0; i < n; i++){ + if(i!=0){ + JsonNode firewallEntry = new ObjectMapper().createObjectNode(); + ((ObjectNode) firewallEntry).put("server", "client" + (i+1)); + firewallConfigArray.add(firewallEntry); + } + firewallNeighbours.put(new Long(i+1), new Neighbour(new Long(i+1), "client" + (i+1))); + } + + firewallNeighbours.put(new Long(n+1), new Neighbour(new Long(n+1), "server")); + + firewall.setConfiguration(new Configuration(firewall.getName(),"", firewallConfigArray)); + + + firewall.setNeighbours(firewallNeighbours ); + //add client to list + nodes.add(firewall); + + for(int i=0; i< n;i++){ + Node client = new Node(); + client.setId(i+1); + client.setName("client" + (i+1)); + client.setFunctional_type("endhost"); + ArrayNode clientConfigArray = new ObjectMapper().createArrayNode(); + JsonNode clientConfig = new ObjectMapper().createObjectNode(); + ((ObjectNode)clientConfig).put("url", "www.facebook.com"); + ((ObjectNode)clientConfig).put("body", "word"); + ((ObjectNode)clientConfig).put("destination","server"); + ((ObjectNode)clientConfig).put("protocol", "HTTP_REQUEST"); + clientConfigArray.add(clientConfig); + + Map clientNeighbours = new HashMap(); + + clientNeighbours.put(1L, new Neighbour(1L, "firewall")); + + client.setNeighbours(clientNeighbours); + client.setConfiguration(new Configuration(client.getName(),"", clientConfigArray)); + + //add client to list + nodes.add(client); + } + + Node server = new Node(); + server.setName("server"); + server.setFunctional_type("webserver"); + ArrayNode serverConfigArray = new ObjectMapper().createArrayNode(); + server.setConfiguration(new Configuration(server.getName(),"", serverConfigArray)); + + Map serverNeighbours = new HashMap(); + serverNeighbours.put(1L, new Neighbour(1L, "firewall")); + server.setNeighbours(serverNeighbours ); + + //add server to list + nodes.add(server); + + //create graph + Graph g = new Graph(); + Map graphNodes = new HashMap(); + long index = 1L; + for (Node node : nodes){ + graphNodes.put(index, node); + index++; + } + g.setNodes(graphNodes); + + return g; + } + + private static Graph generateDpiScenario(int n) { + List nodes = new ArrayList(); + + Node client = new Node(); + client.setName("client"); + client.setFunctional_type("endhost"); + ArrayNode clientConfigArray = new ObjectMapper().createArrayNode(); + JsonNode clientConfig = new ObjectMapper().createObjectNode(); + ((ObjectNode)clientConfig).put("url", "www.facebook.com"); + ((ObjectNode)clientConfig).put("body", "word"); + ((ObjectNode)clientConfig).put("destination","server"); + ((ObjectNode)clientConfig).put("protocol", "HTTP_REQUEST"); + clientConfigArray.add(clientConfig); + client.setConfiguration(new Configuration(client.getName(),"", clientConfigArray)); + + Map clientNeighbours = new HashMap(); + clientNeighbours.put(1L, new Neighbour(1L, "dpi1")); + client.setNeighbours(clientNeighbours ); + //add client to list + nodes.add(client); + + for(int i=0; i< n;i++){ + Node dpi = new Node(); + dpi.setId(i+1); + dpi.setName("dpi" + (i+1)); + dpi.setFunctional_type("dpi"); + ArrayNode configArray = new ObjectMapper().createArrayNode(); + configArray.add("drug"); + + Map natNeighbours = new HashMap(); + + //set left neighbour for each node except the first + if(dpi.getId() != 1){ + natNeighbours.put(1L, new Neighbour(1L, "dpi" + i)); + } + //first firewall: set only client as neighbour and natted node + else{ + natNeighbours.put(1L, new Neighbour(1L, "client")); + } + //set right neighbour for each node except the last + if(dpi.getId() != n){ + natNeighbours.put(2L, new Neighbour(1L, "dpi" + (i+2))); + } + //last firewall: set server as neighbour + else{ + natNeighbours.put(2L, new Neighbour(1L, "server")); + } + + dpi.setNeighbours(natNeighbours); + dpi.setConfiguration(new Configuration(dpi.getName(),"", configArray)); + + //add nat to list + nodes.add(dpi); + } + + Node server = new Node(); + server.setName("server"); + server.setFunctional_type("webserver"); + ArrayNode serverConfigArray = new ObjectMapper().createArrayNode(); + server.setConfiguration(new Configuration(server.getName(),"", serverConfigArray)); + + Map serverNeighbours = new HashMap(); + serverNeighbours.put(1L, new Neighbour(1L, "dpi" + (n))); + server.setNeighbours(serverNeighbours ); + + //add server to list + nodes.add(server); + + //create graph + Graph g = new Graph(); + Map graphNodes = new HashMap(); + long index = 1L; + for (Node node : nodes){ + graphNodes.put(index, node); + index++; + } + g.setNodes(graphNodes); + + return g; + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/test/TestCase.java b/verigraph/src/main/java/it/polito/escape/verify/test/TestCase.java new file mode 100644 index 0000000..a4c85ab --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/test/TestCase.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.test; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +import it.polito.escape.verify.model.Graph; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ "id", "name", "description", "policy_url_parameters", "result", "graph" }) +public class TestCase { + + @JsonProperty("id") + private Integer id; + + @JsonProperty("name") + private String name; + + @JsonProperty("description") + private String description; + + @JsonProperty("policy_url_parameters") + private String policyUrlParameters; + + @JsonProperty("result") + private String result; + + @JsonProperty("graph") + private Graph graph; + + @JsonIgnore + private Map additionalProperties = new HashMap(); + + /** + * @return The id + */ + @JsonProperty("id") + public Integer getId() { + return id; + } + + /** + * @param id + * The id + */ + @JsonProperty("id") + public void setId(Integer id) { + this.id = id; + } + + /** + * @return The name + */ + @JsonProperty("name") + public String getName() { + return name; + } + + /** + * @param name + * The name + */ + @JsonProperty("name") + public void setName(String name) { + this.name = name; + } + + /** + * @return The description + */ + @JsonProperty("description") + public String getDescription() { + return description; + } + + /** + * @param description + * The description + */ + @JsonProperty("description") + public void setDescription(String description) { + this.description = description; + } + + /** + * @return The policyUrlParameters + */ + @JsonProperty("policy_url_parameters") + public String getPolicyUrlParameters() { + return policyUrlParameters; + } + + /** + * @param policyUrlParameters + * The policy_url_parameters + */ + @JsonProperty("policy_url_parameters") + public void setPolicyUrlParameters(String policyUrlParameters) { + this.policyUrlParameters = policyUrlParameters; + } + + /** + * @return The result + */ + @JsonProperty("result") + public String getResult() { + return result; + } + + /** + * @param result + * The result + */ + @JsonProperty("result") + public void setResult(String result) { + this.result = result; + } + + /** + * @return The graph + */ + @JsonProperty("graph") + public Graph getGraph() { + return graph; + } + + /** + * @param graph + * The graph + */ + @JsonProperty("graph") + public void setGraph(Graph graph) { + this.graph = graph; + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @JsonAnySetter + public void setAdditionalProperty(String name, Object value) { + this.additionalProperties.put(name, value); + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/test/TestExecutionException.java b/verigraph/src/main/java/it/polito/escape/verify/test/TestExecutionException.java new file mode 100644 index 0000000..8babe7b --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/test/TestExecutionException.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.test; + + +public class TestExecutionException extends Exception{ + + /** + * + */ + private static final long serialVersionUID = 4749065055436886197L; + + public TestExecutionException(String message){ + super(message); + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/test/Tester.java b/verigraph/src/main/java/it/polito/escape/verify/test/Tester.java new file mode 100644 index 0000000..4b85e1e --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/test/Tester.java @@ -0,0 +1,220 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.ResponseProcessingException; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Response; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.fge.jsonschema.core.exceptions.ProcessingException; +import com.github.fge.jsonschema.main.JsonSchema; + +import it.polito.escape.verify.client.VerifyClient; +import it.polito.escape.verify.client.VerifyClientException; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Verification; +import it.polito.escape.verify.service.ValidationUtils; + +public class Tester { + + private File schema; + + private List testFiles = new ArrayList(); + + private List testCases = new ArrayList(); + + private String target; + + private VerifyClient verifyClient; + + public Tester(String target, File schema, File folder) throws JsonParseException, JsonMappingException, IOException, + Exception { + init(target, schema, folder); + } + + private void init(String target, File schema, File folder) throws JsonParseException, JsonMappingException, + IOException, Exception { + this.target = target; + this.verifyClient = new VerifyClient(this.target); + this.schema = schema; + this.testFiles = getTests(folder); + this.testCases = getTestCases(this.testFiles); + } + + public List getTests(File folder) { + List filesList = new ArrayList(); + + System.out.println("Test folder set to '" + folder.getAbsolutePath() + "'"); + + File[] files = folder.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".json"); + } + }); + + for (File f : files) { + filesList.add(f); + System.out.println("File '" + f.getName() + "' added to test files"); + } + + return filesList; + } + + public List getTestCases(List files) throws JsonParseException, JsonMappingException, IOException, + Exception { + List testCases = new ArrayList(); + + for (File file : files) { + validateTestFile(file); + try { + TestCase tc = new ObjectMapper().readValue(file, TestCase.class); + testCases.add(tc); + } + catch (Exception e) { + throw e; + } + } + + return testCases; + } + + private void runTestCases() throws VerifyClientException, TestExecutionException { + int counter = 0; + for (TestCase tc : this.testCases) { + String result = runTestCase(tc); + if (!result.equals(tc.getResult())) + throw new TestExecutionException("Error running test given in file '" + this.testFiles.get(counter).getName() + + "'. Test returned '" + result + "' instead of '" + tc.getResult() + "'."); + else + System.out.println("Test given in file '" + this.testFiles.get(counter).getName() + "' returned '" + + result + "' as expected"); + counter++; + } + System.out.println("All tests PASSED"); + } + + private String runTestCase(TestCase tc) throws VerifyClientException, TestExecutionException{ + Client client = ClientBuilder.newClient(); + + Graph graph = tc.getGraph(); + Response response = null; + try{ + response = this.verifyClient.createGraph(graph); + } + catch(ResponseProcessingException e){ + throw new TestExecutionException("Response processing has failed: " + e.getResponse().readEntity(String.class)); + } + catch(javax.ws.rs.ProcessingException e){ + throw new TestExecutionException("HTTP request failed"); + } + Graph createdGraph = response.readEntity(Graph.class); + String urlParams = tc.getPolicyUrlParameters(); + WebTarget target = client.target(this.target + "/graphs/" + createdGraph.getId() + "/policy" + urlParams); + + response = target.request().get(); + Verification verification = response.readEntity(Verification.class); + return verification.getResult(); + } + + public void validateTestFile(File testFile) throws Exception { + JsonSchema schemaNode = null; + try { + schemaNode = ValidationUtils.getSchemaNode(schema); + } + catch (IOException e) { + throw new Exception("Unable to load '" + schema.getAbsolutePath() + "' schema file"); + } + catch (ProcessingException e) { + throw new Exception("Unable to resolve '" + schema.getAbsolutePath() + "' schema file as a schema node"); + } + + JsonNode jsonNode; + try { + jsonNode = ValidationUtils.getJsonNode(testFile); + } + catch (IOException e) { + throw new Exception("Unable to load '" + testFile.getAbsolutePath() + "' as a json node"); + } + + try { + ValidationUtils.validateJson(schemaNode, jsonNode); + } + catch (ProcessingException e) { + throw new Exception("There were errors in the validation of file '" + testFile.getAbsolutePath() + + "' against the json schema '" + schema.getAbsolutePath() + "': " + e.getMessage()); + + } + } + + public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException, + VerifyClientException, Exception { + String folderName = System.getProperty("user.dir") + "/tester/testcases"; + File folder = new File(folderName); + if (!folder.exists()) { + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + String s; + do{ + System.out.println("Please enter the testcases folder path: "); + s = in.readLine(); + if (isValidpath(s)){ + folder = new File(s); + break; + } + }while (s != null && s.length() != 0); + if(s == null) + System.exit(0); + } + String schemaName = System.getProperty("user.dir") + "/tester/testcase_schema.json"; + File schema = new File(schemaName); + if (!schema.exists()) { + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + String s; + do{ + System.out.println("Please enter the full path of 'testcase_schema.json': "); + s = in.readLine(); + if (isValidpath(s)){ + folder = new File(s); + break; + } + }while (s != null && s.length() != 0); + if(s == null) + System.exit(0); + } + + Tester tester = new Tester("http://localhost:8080/verify/api", schema, folder); + + tester.runTestCases(); + + } + + private static boolean isValidpath(String s) { + if (s==null) + return false; + File file = new File(s); + return file.exists(); + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/validation/DpiValidator.java b/verigraph/src/main/java/it/polito/escape/verify/validation/DpiValidator.java new file mode 100644 index 0000000..2666755 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/validation/DpiValidator.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.validation; + +import com.fasterxml.jackson.databind.JsonNode; + +import it.polito.escape.verify.model.Configuration; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Node; +import it.polito.escape.verify.validation.exception.ValidationException; + +public class DpiValidator implements ValidationInterface { + + public DpiValidator(){ + + } + + private void validateKey(String key) throws ValidationException { + if (!key.matches("\\w+")) + throw new ValidationException("'" + key + "' is not a valid configuration string for a 'dpi'"); + } + + @Override + public void validate(Graph graph, Node node, Configuration configuration) throws ValidationException { + + JsonNode conf = configuration.getConfiguration(); + + if (!conf.isArray()) { + throw new ValidationException("Configuration of a 'dpi' must be an array"); + } + for (JsonNode key : conf) { + if (!key.isTextual()) + throw new ValidationException("Configuration of a 'dpi' must be an array of strings"); + validateKey(key.asText()); + } + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/validation/EndhostValidator.java b/verigraph/src/main/java/it/polito/escape/verify/validation/EndhostValidator.java new file mode 100644 index 0000000..3d9eb75 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/validation/EndhostValidator.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.validation; + +import it.polito.escape.verify.model.Configuration; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Node; +import it.polito.escape.verify.validation.exception.ValidationException; + + +public class EndhostValidator implements ValidationInterface { + + @Override + public void validate(Graph graph, Node node, Configuration configuration) throws ValidationException { + + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/validation/ValidationInterface.java b/verigraph/src/main/java/it/polito/escape/verify/validation/ValidationInterface.java new file mode 100644 index 0000000..25608dd --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/validation/ValidationInterface.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.validation; + +import it.polito.escape.verify.model.Configuration; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Node; +import it.polito.escape.verify.validation.exception.ValidationException; + +public interface ValidationInterface { + + void validate(Graph graph, Node node, Configuration configuration) throws ValidationException; + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/validation/VpnaccessValidator.java b/verigraph/src/main/java/it/polito/escape/verify/validation/VpnaccessValidator.java new file mode 100644 index 0000000..6abd603 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/validation/VpnaccessValidator.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.validation; + +import com.fasterxml.jackson.databind.JsonNode; + +import it.polito.escape.verify.model.Configuration; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Node; +import it.polito.escape.verify.validation.exception.ValidationException; + +public class VpnaccessValidator implements ValidationInterface { + + @Override + public void validate(Graph graph, Node node, Configuration configuration) throws ValidationException { + JsonNode configurationNode = configuration.getConfiguration(); + + if (!configurationNode.isArray()) + throw new ValidationException("configuration must be an array"); + + for (JsonNode object : configurationNode) { + JsonNode vpnexit = object.get("vpnexit"); + if (!vpnexit.isTextual()) + throw new ValidationException("value corresponding to the key 'vpnexit' must be a string"); + validateObject(graph, node, vpnexit.asText()); + } + + } + + private void validateObject(Graph g, Node node, String field) throws ValidationException { + boolean isValid = false; + for (Node n : g.getNodes().values()) { + if ((n.getFunctional_type().equals("vpnexit")) && (n.getName().equals(field))) + isValid = true; + } + + if (!isValid) + throw new ValidationException("'" + field + "' is not a valid value for the configuration of a type '" + + node.getFunctional_type() + + "'. Please use the name of an existing node of type 'vpnexit'."); + + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/validation/VpnexitValidator.java b/verigraph/src/main/java/it/polito/escape/verify/validation/VpnexitValidator.java new file mode 100644 index 0000000..782e124 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/validation/VpnexitValidator.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.validation; + +import com.fasterxml.jackson.databind.JsonNode; + +import it.polito.escape.verify.model.Configuration; +import it.polito.escape.verify.model.Graph; +import it.polito.escape.verify.model.Node; +import it.polito.escape.verify.validation.exception.ValidationException; + +public class VpnexitValidator implements ValidationInterface { + + @Override + public void validate(Graph graph, Node node, Configuration configuration) throws ValidationException { + JsonNode configurationNode = configuration.getConfiguration(); + + if (!configurationNode.isArray()) + throw new ValidationException("configuration must be an array"); + + for (JsonNode object : configurationNode) { + JsonNode vpnexit = object.get("vpnaccess"); + if (!vpnexit.isTextual()) + throw new ValidationException("value corresponding to the key 'vpnaccess' must be a string"); + validateObject(graph, node, vpnexit.asText()); + } + + } + + private void validateObject(Graph g, Node node, String field) throws ValidationException { + boolean isValid = false; + for (Node n : g.getNodes().values()) { + if ((n.getFunctional_type().equals("vpnaccess")) && (n.getName().equals(field))) + isValid = true; + } + + if (!isValid) + throw new ValidationException("'" + field + "' is not a valid value for the configuration of a type '" + + node.getFunctional_type() + + "'. Please use the name of an existing node of type 'vpnaccess'."); + + } + +} diff --git a/verigraph/src/main/java/it/polito/escape/verify/validation/exception/ValidationException.java b/verigraph/src/main/java/it/polito/escape/verify/validation/exception/ValidationException.java new file mode 100644 index 0000000..8b94748 --- /dev/null +++ b/verigraph/src/main/java/it/polito/escape/verify/validation/exception/ValidationException.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2017 Politecnico di Torino and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Apache License, Version 2.0 + * which accompanies this distribution, and is available at + * http://www.apache.org/licenses/LICENSE-2.0 + *******************************************************************************/ + +package it.polito.escape.verify.validation.exception; + + +public class ValidationException extends Exception { + + /** + * + */ + private static final long serialVersionUID = -8251271078519549101L; + + private String message; + + public ValidationException(String message){ +// super(message); + this.message = message; + } + + @Override + public String getMessage() { + return this.message; + } +} -- cgit 1.2.3-korg