diff --git a/.classpath b/.classpath index b1c91767e..e4f57ce5d 100644 --- a/.classpath +++ b/.classpath @@ -3,11 +3,10 @@ - - - + + diff --git a/build.xml b/build.xml index b22a61dfb..e3d072c4c 100644 --- a/build.xml +++ b/build.xml @@ -5,7 +5,7 @@ - + @@ -18,6 +18,8 @@ + + @@ -41,8 +43,8 @@ - - + + @@ -90,6 +92,7 @@ - - - - - - - + + @@ -332,9 +330,14 @@ - + + + + + + @@ -391,7 +394,7 @@ windowtitle="las2peer Unit Test Documentation" failonerror="yes" encoding="utf8" - classpath="${lib.cp}:${tmp.classes}" + classpath="${lib.cp}:${tmp.classes}:${lib.junit}" > @@ -443,21 +446,21 @@ - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/img/logo/README.md b/img/logo/README.md new file mode 100644 index 000000000..52b473dbe --- /dev/null +++ b/img/logo/README.md @@ -0,0 +1,35 @@ +# las2peer logos + +A collection of the las2peer logo + +## Standard logo +![logo](https://rwth-acis.github.io/las2peer/logo/vector/las2peer-logo.svg) + +| File | Resolution +| ------------- | ------------- | +| [.svg](logo/vector/las2peer-logo.svg) | | +| [.eps](logo/vector/las2peer-logo.eps) | | +| [.pdf](logo/vector/las2peer-logo.pdf) | | +| [.png](logo/bitmap/las2peer-logo-16x16.png) |16x16| +| [.png](logo/bitmap/las2peer-logo-32x32.png) |32x32| +| [.png](logo/bitmap/las2peer-logo-64x64.png) |64x64| +| [.png](logo/bitmap/las2peer-logo-128x128.png) |128x128| + + +## Logo with text + +![logo with text](https://rwth-acis.github.io/las2peer/logo/text/logo_text_black.svg) + + +| File | +| ------------- | +| [Black text](logo/text/logo_text_black.svg) | + + - White text + +![logo with text](https://rwth-acis.github.io/las2peer/logo/text/logo_text_white.svg) + + +| File | +| ------------- | +| [White text](logo/text/logo_text_white.svg) | diff --git a/img/logo/text/logo_text_black.svg b/img/logo/text/logo_text_black.svg new file mode 100644 index 000000000..088bfcfcf --- /dev/null +++ b/img/logo/text/logo_text_black.svg @@ -0,0 +1,411 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + las2peer + + diff --git a/img/logo/text/logo_text_white.svg b/img/logo/text/logo_text_white.svg new file mode 100644 index 000000000..6cea05d42 --- /dev/null +++ b/img/logo/text/logo_text_white.svg @@ -0,0 +1,411 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + las2peer + + diff --git a/ivy/ivy.xml b/ivy/ivy.xml index a9ce9b518..dacc11c94 100644 --- a/ivy/ivy.xml +++ b/ivy/ivy.xml @@ -1,15 +1,17 @@ - + - - + + - - - - - - - + + + + + + + + + diff --git a/ivy/pom_template.xml b/ivy/pom_template.xml new file mode 100644 index 000000000..3c360994c --- /dev/null +++ b/ivy/pom_template.xml @@ -0,0 +1,16 @@ + +${ivy.pom.license} +${ivy.pom.header} + + + 4.0.0 + ${ivy.pom.groupId} + ${ivy.pom.artifactId} + ${ivy.pom.packaging} + ${ivy.pom.version} + ${ivy.pom.name} + ${ivy.pom.description} + ${ivy.pom.url} + ${ivy.now} + \ No newline at end of file diff --git a/src/main/java/i5/las2peer/communication/Message.java b/src/main/java/i5/las2peer/communication/Message.java index b4e24811f..c956fbf57 100644 --- a/src/main/java/i5/las2peer/communication/Message.java +++ b/src/main/java/i5/las2peer/communication/Message.java @@ -1,18 +1,5 @@ package i5.las2peer.communication; -import java.io.Serializable; -import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.Signature; -import java.security.SignatureException; -import java.util.Date; -import java.util.Random; - -import javax.crypto.SecretKey; - -import org.apache.commons.codec.binary.Base64; - import i5.las2peer.p2p.AgentNotKnownException; import i5.las2peer.persistency.EncodingFailedException; import i5.las2peer.persistency.MalformedXMLException; @@ -25,9 +12,28 @@ import i5.las2peer.tools.SerializationException; import i5.las2peer.tools.SerializeTools; import i5.las2peer.tools.XmlTools; -import i5.simpleXML.Element; -import i5.simpleXML.Parser; -import i5.simpleXML.XMLSyntaxException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.Serializable; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.util.Base64; +import java.util.Date; +import java.util.Random; + +import javax.crypto.SecretKey; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + import rice.p2p.commonapi.NodeHandle; /** @@ -40,7 +46,7 @@ * * Therefore, it is necessary, that the generating Thread has access to the private key of the sending agent. * - * When specifiying a topic, the message will be sent to all agents listening to the topic. Since these agents are not + * When specifying a topic, the message will be sent to all agents listening to the topic. Since these agents are not * known, the message will not be encrypted. * */ @@ -133,8 +139,8 @@ public Message() { * @throws SerializationException * */ - public Message(Agent from, Agent to, Serializable data) - throws EncodingFailedException, L2pSecurityException, SerializationException { + public Message(Agent from, Agent to, Serializable data) throws EncodingFailedException, L2pSecurityException, + SerializationException { this(from, to, data, DEFAULT_TIMEOUT); } @@ -149,8 +155,8 @@ public Message(Agent from, Agent to, Serializable data) * @throws L2pSecurityException the private key of the sender is not accessible for signing * @throws SerializationException */ - public Message(Agent from, Agent to, Serializable data, long timeOutMs) - throws EncodingFailedException, L2pSecurityException, SerializationException { + public Message(Agent from, Agent to, Serializable data, long timeOutMs) throws EncodingFailedException, + L2pSecurityException, SerializationException { if (from == null || to == null) { throw new IllegalArgumentException("null not allowed as sender or recipient!"); } @@ -182,8 +188,8 @@ public Message(Agent from, Agent to, Serializable data, long timeOutMs) * @throws L2pSecurityException the private key of the sender is not accessible for signing * @throws SerializationException */ - public Message(Agent from, Agent to, XmlAble data) - throws EncodingFailedException, L2pSecurityException, SerializationException { + public Message(Agent from, Agent to, XmlAble data) throws EncodingFailedException, L2pSecurityException, + SerializationException { this(from, to, data, DEFAULT_TIMEOUT); } @@ -198,8 +204,8 @@ public Message(Agent from, Agent to, XmlAble data) * @throws L2pSecurityException the private key of the sender is not accessible for signing * @throws SerializationException */ - public Message(Agent from, Agent to, XmlAble data, long timeoutMs) - throws EncodingFailedException, L2pSecurityException, SerializationException { + public Message(Agent from, Agent to, XmlAble data, long timeoutMs) throws EncodingFailedException, + L2pSecurityException, SerializationException { sender = from; senderId = from.getId(); recipient = to; @@ -220,8 +226,8 @@ public Message(Agent from, Agent to, XmlAble data, long timeoutMs) * @throws L2pSecurityException * @throws SerializationException */ - public Message(Agent from, long topic, Serializable data) - throws EncodingFailedException, L2pSecurityException, SerializationException { + public Message(Agent from, long topic, Serializable data) throws EncodingFailedException, L2pSecurityException, + SerializationException { this(from, topic, data, DEFAULT_TIMEOUT); } @@ -236,8 +242,8 @@ public Message(Agent from, long topic, Serializable data) * @throws L2pSecurityException * @throws SerializationException */ - public Message(Agent from, long topic, Serializable data, long timeoutMs) - throws EncodingFailedException, L2pSecurityException, SerializationException { + public Message(Agent from, long topic, Serializable data, long timeoutMs) throws EncodingFailedException, + L2pSecurityException, SerializationException { if (from == null) { throw new IllegalArgumentException("null not allowed as sender!"); } @@ -287,8 +293,8 @@ private void finalizeConstructor() throws EncodingFailedException, L2pSecurityEx * @throws L2pSecurityException the private key of the sender is not accessible for signing * @throws SerializationException */ - public Message(Message responseTo, XmlAble data, long timeoutMs) - throws EncodingFailedException, L2pSecurityException, SerializationException { + public Message(Message responseTo, XmlAble data, long timeoutMs) throws EncodingFailedException, + L2pSecurityException, SerializationException { if (!responseTo.isOpen()) { throw new IllegalStateException("the original message has to be open to create a response to it!"); } @@ -318,8 +324,8 @@ public Message(Message responseTo, XmlAble data, long timeoutMs) * @throws L2pSecurityException the private key of the sender is not accessible for signing * @throws EncodingFailedException */ - public Message(Message responseTo, XmlAble data) - throws EncodingFailedException, L2pSecurityException, SerializationException { + public Message(Message responseTo, XmlAble data) throws EncodingFailedException, L2pSecurityException, + SerializationException { this(responseTo, data, DEFAULT_TIMEOUT); } @@ -333,8 +339,8 @@ public Message(Message responseTo, XmlAble data) * @throws L2pSecurityException the private key of the sender is not accessible for signing * @throws SerializationException */ - public Message(Message responseTo, Serializable data, long timeoutMs) - throws EncodingFailedException, L2pSecurityException, SerializationException { + public Message(Message responseTo, Serializable data, long timeoutMs) throws EncodingFailedException, + L2pSecurityException, SerializationException { if (!responseTo.isOpen()) { throw new IllegalStateException("the original message has to be open to create a response to it!"); } @@ -365,8 +371,8 @@ public Message(Message responseTo, Serializable data, long timeoutMs) * @throws L2pSecurityException the private key of the sender is not accessible for signing * @throws SerializationException */ - public Message(Message responseTo, Serializable data) - throws EncodingFailedException, L2pSecurityException, SerializationException { + public Message(Message responseTo, Serializable data) throws EncodingFailedException, L2pSecurityException, + SerializationException { this(responseTo, data, DEFAULT_TIMEOUT); } @@ -386,7 +392,7 @@ private String getContentString() throws SerializationException { sContent = ((XmlAble) content).toXmlString(); } else { typeAttr = "Serializable"; - sContent = Base64.encodeBase64String(SerializeTools.serialize((Serializable) content)); + sContent = Base64.getEncoder().encodeToString(SerializeTools.serialize((Serializable) content)); } String attrs = ""; @@ -557,8 +563,8 @@ public Object getContent() throws L2pSecurityException { /** * open the envelope, i.e. decrypt the content with the private key of the receiving agent * - * The storage has to know an unlocked version of the recipient agent! (i.e. a {@link i5.las2peer.security.AgentContext} - * bound to him. + * The storage has to know an unlocked version of the recipient agent! (i.e. a + * {@link i5.las2peer.security.AgentContext} bound to him. * * @param storage * @throws L2pSecurityException @@ -580,8 +586,7 @@ public void open(AgentStorage storage) throws L2pSecurityException, AgentNotKnow * @throws L2pSecurityException the private key of the receiver has to be unlocked for decryption * @throws AgentNotKnownException */ - public void open(Agent unlockedRecipient, AgentStorage storage) - throws L2pSecurityException, AgentNotKnownException { + public void open(Agent unlockedRecipient, AgentStorage storage) throws L2pSecurityException, AgentNotKnownException { if (isOpen()) { return; } @@ -600,19 +605,21 @@ public void open(Agent unlockedRecipient, AgentStorage storage) } } - Element root = null; try { - String contentString; + byte[] rawContent; if (!isTopic()) { SecretKey contentKey = recipient.decryptSymmetricKey(baContentKey); - contentString = new String(CryptoTools.decryptSymmetric(baEncryptedContent, contentKey), - StandardCharsets.UTF_8); + rawContent = CryptoTools.decryptSymmetric(baEncryptedContent, contentKey); } else { // topics are not encrypted - contentString = new String(baEncryptedContent, StandardCharsets.UTF_8); + rawContent = baEncryptedContent; } - root = Parser.parse(contentString, false); + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document doc = dBuilder.parse(new ByteArrayInputStream(rawContent)); + doc.getDocumentElement().normalize(); + Element root = doc.getDocumentElement(); if (!root.hasAttribute("sender")) { throw new L2pSecurityException("content block needs sender attribute!"); @@ -656,19 +663,17 @@ public void open(Agent unlockedRecipient, AgentStorage storage) } if (root.getAttribute("type").equals("Serializable")) { - content = SerializeTools.deserialize(Base64.decodeBase64(root.getFirstChild().getText())); + content = SerializeTools.deserializeBase64(root.getTextContent()); } else { content = XmlTools.createFromXml(root.getFirstChild().toString(), root.getAttribute("class")); } } catch (CryptoException e) { throw new L2pSecurityException("Crypto-Problems: Unable to open message content", e); - } catch (XMLSyntaxException e) { - throw new L2pSecurityException("xml syntax problems with decryption!", e); } catch (SerializationException e) { throw new L2pSecurityException("deserializiation problems with decryption!", e); } catch (ClassNotFoundException e) { throw new L2pSecurityException("content class missing with decryption!", e); - } catch (MalformedXMLException e) { + } catch (MalformedXMLException | ParserConfigurationException | SAXException | IOException e) { throw new L2pSecurityException("xml syntax problems with decryption!", e); } @@ -691,6 +696,11 @@ public void verifySignature() throws L2pSecurityException { sig.update(contentBytes); if (!sig.verify(baSignature)) { + System.out.println("---------------------"); + System.out.println("Signature invalid. Please report the output text to LAS-353."); + System.out.println("--------------------- [BEGIN] ---------------------"); + System.out.println(this.toXmlString()); // TODO LAS-353 logging; remove when resolved + System.out.println("--------------------- [END] ---------------------"); throw new L2pSecurityException("Signature invalid!"); } } catch (InvalidKeyException e) { @@ -785,16 +795,17 @@ public String toXmlString() { receiver = "to=\"" + recipientId + "\""; encryption = " encryption=\"" + CryptoTools.getSymmetricAlgorithm() + "\""; contentKey = "\t" + Base64.encodeBase64String(baContentKey) + "\n"; + + "\" encoding=\"base64\">" + Base64.getEncoder().encodeToString(baContentKey) + "\n"; } else { receiver = "topic=\"" + topicId + "\""; } return "\n" + sending + "\t" + Base64.encodeBase64String(baEncryptedContent) + "\n" - + contentKey + "\t" - + Base64.encodeBase64String(baSignature) + "\n" + "\n"; + + encryption + " encoding=\"base64\">" + Base64.getEncoder().encodeToString(baEncryptedContent) + + "\n" + contentKey + "\t" + Base64.getEncoder().encodeToString(baSignature) + + "\n" + "\n"; } /** @@ -805,38 +816,19 @@ public String toXmlString() { */ public void setStateFromXml(String xml) throws MalformedXMLException { try { - Element root = Parser.parse(xml); + Element root = XmlTools.getRootElement(xml, "las2peer:message"); - Element sending = null; - if (root.getChildren("sendingNode").hasMoreElements()) { - sending = root.getChildren("sendingNode").nextElement(); + Element sending = XmlTools.getOptionalElement(root, "sendingNode"); + if (sending != null) { if (!"base64".equals(sending.getAttribute("encoding"))) { throw new MalformedXMLException("base64 encoding of sending node expected!"); } - sendingNodeId = SerializeTools.deserializeBase64(sending.getFirstChild().getText()); + sendingNodeId = SerializeTools.deserializeBase64(sending.getTextContent()); } - if (!root.getName().equals("message")) { - throw new MalformedXMLException("message expected!"); - } - Element content; - if (root.getChildren("content").hasMoreElements()) { - content = root.getChildren("content").nextElement(); - } else { - throw new MalformedXMLException("content expected!"); - } - - Element contentKey = null; - if (root.getChildren("contentKey").hasMoreElements()) { - contentKey = root.getChildren("contentKey").nextElement(); - } - - Element signature; - if (root.getChildren("signature").hasMoreElements()) { - signature = root.getChildren("signature").nextElement(); - } else { - throw new MalformedXMLException("signature expected!"); - } + Element content = XmlTools.getSingularElement(root, "content"); + Element contentKey = XmlTools.getOptionalElement(root, "contentKey"); + Element signature = XmlTools.getSingularElement(root, "signature"); if (!root.hasAttribute("from")) { throw new MalformedXMLException("needed from attribute missing!"); @@ -877,10 +869,10 @@ public void setStateFromXml(String xml) throws MalformedXMLException { // sender = AgentStorage.getAgent( Long.parseLong(root.getAttribute ( "from"))); // recipient = AgentStorage.getAgent( Long.parseLong(root.getAttribute ( "to"))); - baEncryptedContent = Base64.decodeBase64(content.getFirstChild().getText()); - baSignature = Base64.decodeBase64(signature.getFirstChild().getText()); + baEncryptedContent = Base64.getDecoder().decode(content.getTextContent()); + baSignature = Base64.getDecoder().decode(signature.getTextContent()); if (contentKey != null) { - baContentKey = Base64.decodeBase64(contentKey.getFirstChild().getText()); + baContentKey = Base64.getDecoder().decode(contentKey.getTextContent()); } timestampMs = Long.parseLong(root.getAttribute("generated")); @@ -890,12 +882,8 @@ public void setStateFromXml(String xml) throws MalformedXMLException { if (root.hasAttribute("responseTo")) { responseToId = Long.parseLong(root.getAttribute("responseTo")); } - - content = null; } catch (NumberFormatException e) { throw new MalformedXMLException("to or from attribute is not a long!", e); - } catch (XMLSyntaxException e) { - throw new MalformedXMLException("Xml parsing problems", e); } catch (SerializationException e) { throw new MalformedXMLException("deserialization problems (sending node id)", e); } diff --git a/src/main/java/i5/las2peer/p2p/LocalNode.java b/src/main/java/i5/las2peer/p2p/LocalNode.java index 7db2ecf6f..37354ec5d 100644 --- a/src/main/java/i5/las2peer/p2p/LocalNode.java +++ b/src/main/java/i5/las2peer/p2p/LocalNode.java @@ -97,9 +97,28 @@ public void registerReceiver(MessageReceiver receiver) throws AgentAlreadyRegisteredException, L2pSecurityException, AgentException { super.registerReceiver(receiver); + if (receiver instanceof Agent) { + Agent agent = (Agent) receiver; + try { + htKnownAgents.put(((Agent) receiver).getId(), agent.toXmlString()); + } catch (SerializationException e) { + throw new AgentException("Could not register agent reciever", e); + } + } + deliverPendingMessages(receiver.getResponsibleForAgentId(), getNodeId()); } + // TODO this code should be here or not? +// @Override +// public void unregisterReceiver(MessageReceiver receiver) throws AgentNotKnownException, NodeException { +// super.unregisterReceiver(receiver); +// if (receiver instanceof Agent) { +// Agent agent = (Agent) receiver; +// htKnownAgents.remove(agent.getId()); +// } +// } + @Override public void sendMessage(Message message, MessageResultListener listener, SendMode mode) { message.setSendingNodeId(this.getNodeId()); @@ -210,8 +229,9 @@ public Object[] findRegisteredAgent(long agentId, int hintOfExpectedCount) throw @Override public Agent getAgent(long id) throws AgentNotKnownException { - if (locallyKnownAgents.hasAgent(id)) { - return locallyKnownAgents.getAgent(id); + Agent anonymous = getAnonymous(); + if (id == anonymous.getId()) { // TODO use isAnonymous, special ID or Classing for identification + return anonymous; } else { synchronized (htKnownAgents) { String xml = htKnownAgents.get(id); @@ -220,9 +240,7 @@ public Agent getAgent(long id) throws AgentNotKnownException { } try { - Agent result = Agent.createFromXml(xml); - locallyKnownAgents.registerAgent(result); - return result; + return Agent.createFromXml(xml); } catch (MalformedXMLException e) { throw new AgentNotKnownException("XML problems with storage!", e); } @@ -249,8 +267,6 @@ public void storeAgent(Agent agent) throws L2pSecurityException, AgentException throw new AgentException("Serialization failed!", e); } - locallyKnownAgents.registerAgent(agent); - htKnownAgents.put(agent.getId(), agentXml); if (agent instanceof UserAgent) { @@ -288,7 +304,6 @@ public void updateAgent(Agent agent) throws AgentException, L2pSecurityException throw new AgentException("Serialization failed!", e); } - locallyKnownAgents.registerAgent(agent); htKnownAgents.put(agent.getId(), agentXml); if (agent instanceof UserAgent) { diff --git a/src/main/java/i5/las2peer/p2p/Node.java b/src/main/java/i5/las2peer/p2p/Node.java index 39c4c40bf..3e9af3871 100644 --- a/src/main/java/i5/las2peer/p2p/Node.java +++ b/src/main/java/i5/las2peer/p2p/Node.java @@ -50,7 +50,6 @@ import i5.las2peer.security.AgentContext; import i5.las2peer.security.AgentException; import i5.las2peer.security.AgentStorage; -import i5.las2peer.security.BasicAgentStorage; import i5.las2peer.security.GroupAgent; import i5.las2peer.security.L2pSecurityException; import i5.las2peer.security.Mediator; @@ -158,7 +157,6 @@ public enum NodeStatus { private String sInformationFileName = DEFAULT_INFORMATION_FILE; private KeyPair nodeKeyPair; - protected BasicAgentStorage locallyKnownAgents; /** * maps names and emails to UserAgents @@ -226,8 +224,6 @@ public Node(L2pClassManager baseClassLoader, boolean standardObserver, boolean m nodeKeyPair = CryptoTools.generateKeyPair(); nodeServiceCache = new NodeServiceCache(this, nodeServiceCacheLifetime, nodeServiceCacheResultCount); - locallyKnownAgents = new BasicAgentStorage(this); - locallyKnownAgents.registerAgent(getAnonymous()); userManager = new UserAgentManager(this); aliasManager = new ServiceAliasManager(this); @@ -581,16 +577,6 @@ public void registerReceiver(MessageReceiver receiver) throw new L2pSecurityException("An agent has to be unlocked for registering at a node"); } - if (!knowsAgentLocally(agent.getId())) { - try { - storeAgent(agent); - } catch (AgentAlreadyRegisteredException e) { - System.out.println( - "Just for notice - not an error: tried to store an already known agent before registering"); - // nothing to do - } - } - try { // ensure (unlocked) context getAgentContext((Agent) receiver); @@ -1018,16 +1004,6 @@ public boolean hasAgent(long id) { } } - /** - * Checks, if an agent of the given id is known locally. - * - * @param agentId - * @return true, if this agent is (already) known here at this node - */ - public boolean knowsAgentLocally(long agentId) { - return locallyKnownAgents.hasAgent(agentId); - } - /** * Gets a local registered agent by its id. * diff --git a/src/main/java/i5/las2peer/p2p/NodeInformation.java b/src/main/java/i5/las2peer/p2p/NodeInformation.java index d1fd04cd4..cd1f5b0aa 100644 --- a/src/main/java/i5/las2peer/p2p/NodeInformation.java +++ b/src/main/java/i5/las2peer/p2p/NodeInformation.java @@ -3,9 +3,12 @@ import java.io.IOException; import java.io.Serializable; import java.security.PublicKey; -import java.util.Enumeration; import java.util.Vector; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + import i5.las2peer.persistency.MalformedXMLException; import i5.las2peer.persistency.VerificationFailedException; import i5.las2peer.persistency.XmlAble; @@ -15,9 +18,7 @@ import i5.las2peer.tools.FileContentReader; import i5.las2peer.tools.SerializationException; import i5.las2peer.tools.SerializeTools; -import i5.simpleXML.Element; -import i5.simpleXML.Parser; -import i5.simpleXML.XMLSyntaxException; +import i5.las2peer.tools.XmlTools; /** * A NodeInformation gives basic information about a node. @@ -267,10 +268,8 @@ public String toString() { * @return the node information contained in the given XML file * @throws MalformedXMLException * @throws IOException - * @throws XMLSyntaxException */ - public static NodeInformation createFromXmlFile(String filename) - throws MalformedXMLException, IOException, XMLSyntaxException { + public static NodeInformation createFromXmlFile(String filename) throws MalformedXMLException, IOException { return createFromXml(FileContentReader.read(filename)); } @@ -282,12 +281,11 @@ public static NodeInformation createFromXmlFile(String filename) * @return a node information * * - * @throws XMLSyntaxException * @throws MalformedXMLException * @throws IOException */ public static NodeInformation createFromXmlFile(String filename, ServiceAgent[] serviceAgents) - throws XMLSyntaxException, MalformedXMLException, IOException { + throws MalformedXMLException, IOException { NodeInformation result = createFromXmlFile(filename); result.setServices(serviceAgents); @@ -300,50 +298,56 @@ public static NodeInformation createFromXmlFile(String filename, ServiceAgent[] * @param xml * @return node information contained in the given XML string * @throws MalformedXMLException - * @throws XMLSyntaxException */ - public static NodeInformation createFromXml(String xml) throws MalformedXMLException, XMLSyntaxException { - Element root = Parser.parse(xml, false); - if (!root.getName().equals("las2peerNode")) { - throw new MalformedXMLException("not a node information but a " + root.getName()); - } + public static NodeInformation createFromXml(String xml) throws MalformedXMLException { + Element root = XmlTools.getRootElement(xml, "las2peerNode"); NodeInformation result = new NodeInformation(); try { - Enumeration children = root.getChildren(); - while (children.hasMoreElements()) { - Element child = children.nextElement(); - - if (child.getName().equals("adminName")) { - result.adminName = child.getFirstChild().getText(); - } else if (child.getName().equals("adminEmail")) { - result.adminEmail = child.getFirstChild().getText(); - } else if (child.getName().equals("organization")) { - result.organization = child.getFirstChild().getText(); - } else if (child.getName().equals("description")) { - result.description = child.getFirstChild().getText(); - } else if (child.getName().equals("nodeHandle")) { - result.nodeHandle = SerializeTools.deserializeBase64(child.getChild(1).getFirstChild().getText()); - } else if (child.getName().equals("nodeKey")) { - result.nodeKey = (PublicKey) SerializeTools.deserializeBase64(child.getFirstChild().getText()); - } else if (child.getName().equals("signature")) { - result.signature = (byte[]) SerializeTools.deserializeBase64(child.getFirstChild().getText()); - } else if (child.getName().equals("services")) { + NodeList children = root.getChildNodes(); + for (int c = 0; c < children.getLength(); c++) { + Node node = children.item(c); + if (node.getNodeType() != Node.ELEMENT_NODE) { + // XXX logging + continue; + } + Element child = (Element) node; + + if (child.getTagName().equals("adminName")) { + result.adminName = child.getTextContent(); + } else if (child.getTagName().equals("adminEmail")) { + result.adminEmail = child.getTextContent(); + } else if (child.getTagName().equals("organization")) { + result.organization = child.getTextContent(); + } else if (child.getTagName().equals("description")) { + result.description = child.getTextContent(); + } else if (child.getTagName().equals("nodeHandle")) { + Element serializedNodeHandle = XmlTools.getSingularElement(child, "serialized"); + result.nodeHandle = SerializeTools.deserializeBase64(serializedNodeHandle.getTextContent()); + } else if (child.getTagName().equals("nodeKey")) { + result.nodeKey = (PublicKey) SerializeTools.deserializeBase64(child.getTextContent()); + } else if (child.getTagName().equals("signature")) { + result.signature = (byte[]) SerializeTools.deserializeBase64(child.getTextContent()); + } else if (child.getTagName().equals("services")) { Vector serviceClasses = new Vector(); - - Enumeration services = child.getChildren(); - while (services.hasMoreElements()) { - Element service = services.nextElement(); - if (!service.getName().equals("serviceClass")) { + NodeList services = child.getChildNodes(); + for (int s = 0; s < services.getLength(); s++) { + Node serviceNode = services.item(s); + if (node.getNodeType() != Node.ELEMENT_NODE) { + // XXX logging + continue; + } + Element service = (Element) serviceNode; + if (!service.getTagName().equals("serviceClass")) { throw new MalformedXMLException(service + " is not a service class element"); } - serviceClasses.add(service.getFirstChild().getText()); + serviceClasses.add(service.getTextContent()); } result.hostedServices = serviceClasses.toArray(new ServiceNameVersion[0]); } else { - throw new MalformedXMLException("unknown xml element: " + child.getName()); + throw new MalformedXMLException("unknown xml element: " + child.getTagName()); } } diff --git a/src/main/java/i5/las2peer/p2p/NodeServiceCache.java b/src/main/java/i5/las2peer/p2p/NodeServiceCache.java index 02146751e..d14e0c17c 100644 --- a/src/main/java/i5/las2peer/p2p/NodeServiceCache.java +++ b/src/main/java/i5/las2peer/p2p/NodeServiceCache.java @@ -295,10 +295,12 @@ private boolean update(ServiceNameVersion service, boolean exact, Agent acting) try { res.open(acting, runningAt); } catch (Exception e) { + // XXX logging continue; } if (!(res.getContent() instanceof ServiceDiscoveryContent)) { + // XXX logging continue; } diff --git a/src/main/java/i5/las2peer/p2p/PastryNodeImpl.java b/src/main/java/i5/las2peer/p2p/PastryNodeImpl.java index dc9fee173..f2e299873 100644 --- a/src/main/java/i5/las2peer/p2p/PastryNodeImpl.java +++ b/src/main/java/i5/las2peer/p2p/PastryNodeImpl.java @@ -18,6 +18,7 @@ import java.util.Vector; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.logging.Level; import i5.las2peer.api.StorageCollisionHandler; import i5.las2peer.api.StorageEnvelopeHandler; @@ -28,6 +29,7 @@ import i5.las2peer.classLoaders.L2pClassManager; import i5.las2peer.communication.Message; import i5.las2peer.communication.MessageException; +import i5.las2peer.logging.L2pLogger; import i5.las2peer.logging.NodeObserver.Event; import i5.las2peer.p2p.pastry.MessageEnvelope; import i5.las2peer.p2p.pastry.NodeApplication; @@ -60,6 +62,8 @@ */ public class PastryNodeImpl extends Node { + private static final L2pLogger logger = L2pLogger.getInstance(PastryNodeImpl.class); + private static final int AGENT_GET_TIMEOUT = 10000; private static final int AGENT_STORE_TIMEOUT = 10000; private static final int ARTIFACT_GET_TIMEOUT = 10000; @@ -122,15 +126,24 @@ public PastryNodeImpl(L2pClassManager classLoader, boolean useMonitoringObserver * be random. */ public PastryNodeImpl(String bootstrap, STORAGE_MODE storageMode, String storageDir, Long nodeIdSeed) { - super(null, true, false); + this(null, null, bootstrap, storageMode, storageDir, nodeIdSeed); + } + + public PastryNodeImpl(L2pClassManager classManager, Integer port, String bootstrap, STORAGE_MODE storageMode, + String storageDir, Long nodeIdSeed) { + super(classManager, true, false); pastryBindAddress = InetAddress.getLoopbackAddress(); - // use system defined port - try { - ServerSocket tmpSocket = new ServerSocket(0); - tmpSocket.close(); - pastryPort = tmpSocket.getLocalPort(); - } catch (IOException e) { - throw new RuntimeException(e); + if (port == null) { + // use system defined port + try { + ServerSocket tmpSocket = new ServerSocket(0); + tmpSocket.close(); + pastryPort = tmpSocket.getLocalPort(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + pastryPort = port; } this.bootStrap = bootstrap; this.mode = storageMode; @@ -155,7 +168,7 @@ private void setupPastryEnvironment() { break; } } catch (Exception e) { - // XXX logging + logger.log(Level.FINER, "Exception while checking for config file '" + filename + "'", e); } } Hashtable properties = new Hashtable<>(); @@ -188,6 +201,9 @@ private void setupPastryEnvironment() { if (pastryBindAddress != null && pastryBindAddress.isLoopbackAddress()) { properties.put("allow_loopback_address", "1"); } + if (!properties.containsKey("p2p_past_messageTimeout")) { + properties.put("p2p_past_messageTimeout", "120000"); + } if (!properties.containsKey("pastry_socket_known_network_address")) { if (!properties.containsKey("pastry_socket_known_network_address_port")) { properties.put("pastry_socket_known_network_address_port", "80"); @@ -198,8 +214,7 @@ private void setupPastryEnvironment() { } for (String prop : properties.keySet()) { pastryEnvironment.getParameters().setString(prop, properties.get(prop)); - // XXX logging - System.out.println("setting: " + prop + ": '" + properties.get(prop) + "'"); + logger.info("setting: " + prop + ": '" + properties.get(prop) + "'"); } } @@ -458,9 +473,11 @@ public void sendMessage(Message message, Object atNodeId, MessageResultListener try { application.sendMessage(new MessageEnvelope(pastryNode.getLocalHandle(), message), (NodeHandle) atNodeId); } catch (MalformedXMLException e) { + logger.log(Level.SEVERE, "Can't read message XML", e); observerNotice(Event.MESSAGE_FAILED, pastryNode, message.getSenderId(), atNodeId, message.getRecipientId(), "XML exception!"); } catch (MessageException e) { + logger.log(Level.SEVERE, "Could not send message", e); observerNotice(Event.MESSAGE_FAILED, pastryNode, message.getSenderId(), atNodeId, message.getRecipientId(), "Message exception!"); } @@ -519,20 +536,26 @@ public NodeApplication getApplication() { @Override public Agent getAgent(long id) throws AgentNotKnownException { - if (locallyKnownAgents.hasAgent(id)) { - return locallyKnownAgents.getAgent(id); - } else { - observerNotice(Event.AGENT_GET_STARTED, pastryNode, id, null, (Long) null, ""); - try { + // no caching here, because agents may have changed in the network + observerNotice(Event.AGENT_GET_STARTED, pastryNode, id, null, (Long) null, ""); + try { + Agent agentFromNet = null; + Agent anonymous = getAnonymous(); + if (id == anonymous.getId()) { // TODO use isAnonymous, special ID or Classing for identification + agentFromNet = anonymous; + } else { Envelope agentEnvelope = pastStorage.fetchEnvelope(Envelope.getAgentIdentifier(id), AGENT_GET_TIMEOUT); - Agent agentFromNet = Agent.createFromXml((String) agentEnvelope.getContent()); - observerNotice(Event.AGENT_GET_SUCCESS, pastryNode, id, null, (Long) null, ""); - locallyKnownAgents.registerAgent(agentFromNet); - return agentFromNet; - } catch (Exception e) { - observerNotice(Event.AGENT_GET_FAILED, pastryNode, id, null, (Long) null, ""); - throw new AgentNotKnownException("Unable to retrieve Agent " + id + " from past storage", e); + agentFromNet = Agent.createFromXml((String) agentEnvelope.getContent()); } + observerNotice(Event.AGENT_GET_SUCCESS, pastryNode, id, null, (Long) null, ""); + return agentFromNet; + } catch (Exception e) { + // TODO bad exception handling + // actually ArtifactNotFoundException is the only one that should cause an AgentNotKnownException here + // maybe the interface should be changed to throw an AgentException instead + logger.log(Level.WARNING, "Unable to retrieve Agent " + id + " from past storage", e); + observerNotice(Event.AGENT_GET_FAILED, pastryNode, id, null, (Long) null, ""); + throw new AgentNotKnownException("Unable to retrieve Agent " + id + " from past storage", e); } } @@ -542,17 +565,8 @@ public void storeAgent(Agent agent) throws L2pSecurityException, AgentException throw new L2pSecurityException("You have to unlock the agent before storage!"); // because the agent has to sign itself } - if (locallyKnownAgents.hasAgent(agent.getId())) { - throw new AgentAlreadyRegisteredException("This agent is already known locally!"); - } + // TODO check if anonymous should be stored and deny observerNotice(Event.AGENT_UPLOAD_STARTED, pastryNode, agent, ""); - try { - Agent stored = getAgent(agent.getId()); - observerNotice(Event.AGENT_UPLOAD_FAILED, pastryNode, agent, "Agent already known!"); - throw new AgentAlreadyRegisteredException("I already know stored version: " + stored); - } catch (AgentNotKnownException e) { - } - locallyKnownAgents.registerAgent(agent); try { Envelope agentEnvelope = null; try { @@ -569,7 +583,6 @@ public void storeAgent(Agent agent) throws L2pSecurityException, AgentException } observerNotice(Event.AGENT_UPLOAD_SUCCESS, pastryNode, agent, ""); } catch (CryptoException | SerializationException | StorageException e) { - locallyKnownAgents.unregisterAgent(agent); observerNotice(Event.AGENT_UPLOAD_FAILED, pastryNode, agent, "Got interrupted!"); throw new AgentException("Storage has been interrupted", e); } diff --git a/src/main/java/i5/las2peer/persistency/Envelope.java b/src/main/java/i5/las2peer/persistency/Envelope.java index f883b86db..561d45582 100644 --- a/src/main/java/i5/las2peer/persistency/Envelope.java +++ b/src/main/java/i5/las2peer/persistency/Envelope.java @@ -4,7 +4,6 @@ import java.security.PublicKey; import java.util.Base64; import java.util.Collection; -import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Map.Entry; @@ -13,6 +12,10 @@ import javax.crypto.SecretKey; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + import i5.las2peer.execution.L2pThread; import i5.las2peer.logging.L2pLogger; import i5.las2peer.p2p.AgentNotKnownException; @@ -25,9 +28,7 @@ import i5.las2peer.tools.CryptoTools; import i5.las2peer.tools.SerializationException; import i5.las2peer.tools.SerializeTools; -import i5.simpleXML.Element; -import i5.simpleXML.Parser; -import i5.simpleXML.XMLSyntaxException; +import i5.las2peer.tools.XmlTools; public class Envelope implements Serializable, XmlAble { @@ -318,81 +319,78 @@ public String toXmlString() throws SerializationException { /** * factory for generating an envelope from the given XML String representation * - * @param root + * @param rootElement * @return envelope created from the given XML String serialization * @throws MalformedXMLException */ - public static Envelope createFromXml(Element root) throws MalformedXMLException { - try { - if (!root.getName().equals("envelope")) { - throw new MalformedXMLException("not an envelope"); - } - if (!root.hasAttribute("identifier")) { - throw new MalformedXMLException("identifier attribute expected!"); - } - if (!root.hasAttribute("version")) { - throw new MalformedXMLException("version attribute expected!"); - } - String identifier = root.getAttribute("identifier"); - long version = Long.parseLong(root.getAttribute("version")); - Element content = root.getFirstChild(); - if (!content.getName().equals("content")) { - throw new MalformedXMLException("envelope content expected"); - } - if (!content.getAttribute("encoding").equals("Base64")) { - throw new MalformedXMLException("base 64 encoding of the content expected"); - } - byte[] rawContent = Base64.getDecoder().decode(content.getFirstChild().getText()); - Element keys = root.getChild(1); - if (!keys.getName().equals("keys")) { - throw new MalformedXMLException("not an envelope"); + public static Envelope createFromXml(Element rootElement) throws MalformedXMLException { + if (!rootElement.hasAttribute("identifier")) { + throw new MalformedXMLException("identifier attribute expected!"); + } + String identifier = rootElement.getAttribute("identifier"); + if (!rootElement.hasAttribute("version")) { + throw new MalformedXMLException("version attribute expected!"); + } + long version = Long.parseLong(rootElement.getAttribute("version")); + // read content from XML + Element content = XmlTools.getSingularElement(rootElement, "las2peer:content"); + if (!content.getAttribute("encoding").equals("Base64")) { + throw new MalformedXMLException("base 64 encoding of the content expected"); + } + byte[] rawContent = Base64.getDecoder().decode(content.getTextContent()); + // read reader keys from XML + Element keys = XmlTools.getSingularElement(rootElement, "las2peer:keys"); + if (!keys.getAttribute("encoding").equalsIgnoreCase("base64")) { + throw new MalformedXMLException( + "base 64 encoding of the content expected - got: " + keys.getAttribute("encoding")); + } + if (!keys.getAttribute("encryption").equalsIgnoreCase(CryptoTools.getAsymmetricAlgorithm())) { + throw new MalformedXMLException( + CryptoTools.getAsymmetricAlgorithm() + " encryption of the content expected"); + } + HashMap readerKeys = new HashMap<>(); + NodeList enKeys = keys.getChildNodes(); + for (int n = 0; n < enKeys.getLength(); n++) { + Node node = enKeys.item(n); + if (node.getNodeType() != Node.ELEMENT_NODE) { + // XXX logging + continue; } - if (!keys.getAttribute("encoding").equals("base64")) { - throw new MalformedXMLException( - "base 64 encoding of the content expected - got: " + keys.getAttribute("encoding")); + Element key = (Element) node; + if (!key.getNodeName().equals("las2peer:key")) { + throw new MalformedXMLException("key expected"); } - if (!keys.getAttribute("encryption").equals(CryptoTools.getAsymmetricAlgorithm())) { - throw new MalformedXMLException( - CryptoTools.getAsymmetricAlgorithm() + " encryption of the content expected"); + String strPublicKey = key.getAttribute("public"); + try { + PublicKey publicKey = CryptoTools.stringToPublicKey(strPublicKey); + byte[] encryptedReaderKey = Base64.getDecoder().decode(key.getFirstChild().getTextContent()); + readerKeys.put(publicKey, encryptedReaderKey); + } catch (CryptoException e) { + throw new MalformedXMLException("Could not convert string to public key", e); } - // reader keys - HashMap readerKeys = new HashMap<>(); - for (Enumeration enKeys = keys.getChildren(); enKeys.hasMoreElements();) { - Element key = enKeys.nextElement(); - if (!key.getName().equals("key")) { - throw new MalformedXMLException("key expected"); - } - String strPublicKey = key.getAttribute("public"); - try { - PublicKey publicKey = CryptoTools.stringToPublicKey(strPublicKey); - byte[] encryptedReaderKey = Base64.getDecoder().decode(key.getFirstChild().getText()); - readerKeys.put(publicKey, encryptedReaderKey); - } catch (CryptoException e) { - throw new MalformedXMLException("Could not convert string to public key", e); - } + } + // groups + Element groups = XmlTools.getSingularElement(rootElement, "las2peer:groups"); + HashSet readerGroupIds = new HashSet<>(); + NodeList enGroups = groups.getChildNodes(); + for (int n = 0; n < enGroups.getLength(); n++) { + Node node = enKeys.item(n); + if (node.getNodeType() != Node.ELEMENT_NODE) { + // XXX logging + continue; } - // groups - Element groups = root.getChild(2); - if (!groups.getName().equals("groups")) { - throw new MalformedXMLException("groups tag expected"); + Element group = (Element) node; + if (!group.getNodeName().equals("las2peer:group")) { + throw new MalformedXMLException("group expected"); } - HashSet readerGroupIds = new HashSet<>(); - for (Enumeration enGroups = groups.getChildren(); enGroups.hasMoreElements();) { - Element group = enGroups.nextElement(); - if (!group.getName().equals("group")) { - throw new MalformedXMLException("group expected"); - } - if (!group.hasAttribute("id")) { - throw new MalformedXMLException("group id expected"); - } - long groupId = Long.valueOf(group.getAttribute("id")); - readerGroupIds.add(groupId); + if (!group.hasAttribute("id")) { + throw new MalformedXMLException("group id expected"); } - return new Envelope(identifier, version, readerKeys, readerGroupIds, rawContent); - } catch (XMLSyntaxException e) { - throw new MalformedXMLException("problems with parsing the XML document", e); + long groupId = Long.valueOf(group.getAttribute("id")); + readerGroupIds.add(groupId); } + return new Envelope(identifier, version, readerKeys, readerGroupIds, rawContent); } /** @@ -403,11 +401,7 @@ public static Envelope createFromXml(Element root) throws MalformedXMLException * @throws MalformedXMLException */ public static Envelope createFromXml(String xml) throws MalformedXMLException { - try { - return createFromXml(Parser.parse(xml, false)); - } catch (XMLSyntaxException e) { - throw new MalformedXMLException("problems with parsing the xml document", e); - } + return createFromXml(XmlTools.getRootElement(xml, "las2peer:envelope")); } } diff --git a/src/main/java/i5/las2peer/security/Agent.java b/src/main/java/i5/las2peer/security/Agent.java index b258d6151..3499890a8 100644 --- a/src/main/java/i5/las2peer/security/Agent.java +++ b/src/main/java/i5/las2peer/security/Agent.java @@ -1,15 +1,17 @@ package i5.las2peer.security; +import java.io.File; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; +import java.util.Base64; import javax.crypto.SecretKey; -import org.apache.commons.codec.binary.Base64; +import org.w3c.dom.Element; import i5.las2peer.communication.Message; import i5.las2peer.communication.MessageException; @@ -21,9 +23,7 @@ import i5.las2peer.tools.CryptoTools; import i5.las2peer.tools.SerializationException; import i5.las2peer.tools.SerializeTools; -import i5.simpleXML.Element; -import i5.simpleXML.Parser; -import i5.simpleXML.XMLSyntaxException; +import i5.las2peer.tools.XmlTools; /** * An Agent is the basic acting entity in the las2peer network. At the moment, an agent can represent a simple user, a @@ -225,7 +225,7 @@ public byte[] signContent(byte[] plainData) throws CryptoException, L2pSecurityE * @return encoded version or the private key */ protected String getEncodedPrivate() { - return Base64.encodeBase64String(baEncrypedPrivate); + return Base64.getEncoder().encodeToString(baEncrypedPrivate); } /** @@ -289,6 +289,20 @@ public Node getRunningAtNode() { return runningAt; } + /** + * Factory: Create an agent from its XML file representation. + * + * Depending on the type attribute of the root node, the type will be a {@link UserAgent}, {@link GroupAgent}, + * {@link ServiceAgent}. Creation of {@link MonitoringAgent}s is not supported. + * + * @param xmlFile + * @return an agent + * @throws MalformedXMLException + */ + public static Agent createFromXml(File xmlFile) throws MalformedXMLException { + return createFromXml(XmlTools.getRootElement(xmlFile, "las2peer:agent")); + } + /** * Factory: Create an agent from its XML string representation. * @@ -300,11 +314,7 @@ public Node getRunningAtNode() { * @throws MalformedXMLException */ public static Agent createFromXml(String xml) throws MalformedXMLException { - try { - return createFromXml(Parser.parse(xml, false)); - } catch (XMLSyntaxException e) { - throw new MalformedXMLException("Error parsing xml string", e); - } + return createFromXml(XmlTools.getRootElement(xml, "las2peer:agent")); } /** @@ -313,32 +323,22 @@ public static Agent createFromXml(String xml) throws MalformedXMLException { * Depending on the type attribute of the root node, the type will be a {@link UserAgent}, {@link GroupAgent}, * {@link ServiceAgent}. Creation of {@link MonitoringAgent}s is not supported. * - * @param root + * @param rootElement * @return an agent * @throws MalformedXMLException */ - public static Agent createFromXml(Element root) throws MalformedXMLException { - try { - if (!root.getName().equals("agent")) { - throw new MalformedXMLException("this is not an agent but a " + root.getName()); - } - - String type = root.getAttribute("type"); - - if ("user".equals(type)) { - return UserAgent.createFromXml(root); - } else if ("group".equals(type)) { - return GroupAgent.createFromXml(root); - } else if ("service".equals(type)) { - return ServiceAgent.createFromXml(root); - } else if ("monitoring".equals(type)) { - return MonitoringAgent.createFromXml(root); - } else { - throw new MalformedXMLException("Unknown agent type: " + type); - } - - } catch (XMLSyntaxException e) { - throw new MalformedXMLException("Error parsing xml string", e); + public static Agent createFromXml(Element rootElement) throws MalformedXMLException { + String type = rootElement.getAttribute("type"); + if ("user".equalsIgnoreCase(type)) { + return UserAgent.createFromXml(rootElement); + } else if ("group".equalsIgnoreCase(type)) { + return GroupAgent.createFromXml(rootElement); + } else if ("service".equalsIgnoreCase(type)) { + return ServiceAgent.createFromXml(rootElement); + } else if ("monitoring".equalsIgnoreCase(type)) { + return MonitoringAgent.createFromXml(rootElement); + } else { + throw new MalformedXMLException("Unknown agent type: " + type); } } diff --git a/src/main/java/i5/las2peer/security/GroupAgent.java b/src/main/java/i5/las2peer/security/GroupAgent.java index 0b57f2628..959682ef2 100755 --- a/src/main/java/i5/las2peer/security/GroupAgent.java +++ b/src/main/java/i5/las2peer/security/GroupAgent.java @@ -1,5 +1,19 @@ package i5.las2peer.security; +import java.io.Serializable; +import java.security.KeyPair; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collections; +import java.util.Hashtable; +import java.util.Random; + +import javax.crypto.SecretKey; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + import i5.las2peer.communication.Message; import i5.las2peer.communication.MessageException; import i5.las2peer.logging.L2pLogger; @@ -12,22 +26,7 @@ import i5.las2peer.tools.CryptoTools; import i5.las2peer.tools.SerializationException; import i5.las2peer.tools.SerializeTools; -import i5.simpleXML.Element; -import i5.simpleXML.Parser; -import i5.simpleXML.XMLSyntaxException; - -import java.io.Serializable; -import java.security.KeyPair; -import java.security.PublicKey; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Random; - -import javax.crypto.SecretKey; - -import org.apache.commons.codec.binary.Base64; +import i5.las2peer.tools.XmlTools; /** * An agent representing a group of other agents. @@ -71,13 +70,14 @@ protected GroupAgent(long id, PublicKey pubKey, byte[] encryptedPrivate, Hashtab * @throws CryptoException * @throws SerializationException */ - protected GroupAgent(long id, KeyPair keys, SecretKey secret, Agent[] members) throws L2pSecurityException, - CryptoException, SerializationException { + protected GroupAgent(long id, KeyPair keys, SecretKey secret, Agent[] members) + throws L2pSecurityException, CryptoException, SerializationException { super(id, keys, secret); symmetricGroupKey = secret; - for (Agent a : members) + for (Agent a : members) { addMember(a, false); + } lockPrivateKey(); } @@ -105,8 +105,8 @@ public void unlockPrivateKey(Agent agent) throws L2pSecurityException, Serializa * @throws SerializationException * @throws CryptoException */ - public void unlockPrivateKeyRecursive(Agent agent, AgentStorage agentStorage) throws L2pSecurityException, - SerializationException, CryptoException { + public void unlockPrivateKeyRecursive(Agent agent, AgentStorage agentStorage) + throws L2pSecurityException, SerializationException, CryptoException { if (isMember(agent)) { unlockPrivateKey(agent); return; @@ -138,8 +138,9 @@ public void unlockPrivateKeyRecursive(Agent agent, AgentStorage agentStorage) th private void decryptSecretKey(Agent agent) throws SerializationException, CryptoException, L2pSecurityException { byte[] crypted = htEncryptedKeyVersions.get(agent.getId()); - if (crypted == null) + if (crypted == null) { throw new L2pSecurityException("the given agent is not listed as a group member!"); + } symmetricGroupKey = agent.decryptSymmetricKey(crypted); } @@ -166,10 +167,11 @@ public void addMember(Agent a) throws L2pSecurityException, CryptoException, Ser * @throws SerializationException * @throws CryptoException */ - private final void addMember(Agent a, boolean securityCheck) throws L2pSecurityException, CryptoException, - SerializationException { - if (securityCheck && isLocked()) + private final void addMember(Agent a, boolean securityCheck) + throws L2pSecurityException, CryptoException, SerializationException { + if (securityCheck && isLocked()) { throw new L2pSecurityException("you have to unlock this group first!"); + } byte[] cryptedSecret = CryptoTools.encryptAsymmetric(symmetricGroupKey, a.getPublicKey()); htEncryptedKeyVersions.put(a.getId(), cryptedSecret); @@ -331,15 +333,17 @@ public void removeMemberRecursive(Agent a) throws L2pSecurityException { * @throws L2pSecurityException */ public void removeMember(long id) throws L2pSecurityException { - if (isLocked()) + if (isLocked()) { throw new L2pSecurityException("You have to unlock this agent first!"); + } htEncryptedKeyVersions.remove(id); } public void removeMemberRecursive(long id) throws L2pSecurityException { - if (isLocked()) + if (isLocked()) { throw new L2pSecurityException("You have to unlock this agent first!"); + } htEncryptedKeyVersions.remove(id); for (Long memberId : htEncryptedKeyVersions.keySet()) { @@ -369,7 +373,7 @@ public String toXmlString() { for (Long id : htEncryptedKeyVersions.keySet()) { keyList += "\t\t" - + Base64.encodeBase64String(htEncryptedKeyVersions.get(id)) + "\n"; + + Base64.getEncoder().encodeToString(htEncryptedKeyVersions.get(id)) + "\n"; } StringBuffer result = new StringBuffer("\n" + "\t" + getId() + "\n" @@ -395,29 +399,18 @@ public String toXmlString() { } /** - * factory - create an instance of GroupAgent from its xml representation + * factory - create an instance of GroupAgent from its XML representation * * @param xml * @return a group agent * @throws MalformedXMLException */ public static GroupAgent createFromXml(String xml) throws MalformedXMLException { - try { - Element root = Parser.parse(xml, false); - - if (!"group".equals(root.getAttribute("type"))) - throw new MalformedXMLException("group agent expeced"); - if (!"agent".equals(root.getName())) - throw new MalformedXMLException("agent expected"); - - return createFromXml(root); - } catch (XMLSyntaxException e) { - throw new MalformedXMLException("Error parsing xml string", e); - } + return createFromXml(XmlTools.getRootElement(xml, "las2peer:agent")); } /** - * factory - create an instance of GroupAgent based on a xml node + * factory - create an instance of GroupAgent based on a XML node * * @param root * @return a group agent @@ -425,97 +418,65 @@ public static GroupAgent createFromXml(String xml) throws MalformedXMLException */ public static GroupAgent createFromXml(Element root) throws MalformedXMLException { try { - Element elId = null; - Element pubKey = null; - Element privKey = null; - Element encryptedKeys = null; - Element groupname = null; - Element userdata = null; - - Enumeration children = root.getChildren(); - while (children.hasMoreElements()) { - Element next = children.nextElement(); - String name = next.getName(); - if (name.equals("id")) { - elId = next; - } else if (name.equals("publickey")) { - pubKey = next; - } else if (name.equals("privatekey")) { - privKey = next; - } else if (name.equals("unlockKeys")) { - encryptedKeys = next; - } else if (name.equals("groupname")) { - groupname = next; - } else if (name.equals("userdata")) { - userdata = next; - } - } - - if (elId == null) { - throw new MalformedXMLException("element id expected"); - } - - if (pubKey == null) { - throw new MalformedXMLException("public key expected"); - } + // read id field from XML + Element elId = XmlTools.getSingularElement(root, "id"); + long id = Long.parseLong(elId.getTextContent()); + // read public key from XML + Element pubKey = XmlTools.getSingularElement(root, "publickey"); if (!pubKey.getAttribute("encoding").equals("base64")) { throw new MalformedXMLException("base64 encoding expected"); } - - if (privKey == null) { - throw new MalformedXMLException("private key expected"); - } + PublicKey publicKey = (PublicKey) SerializeTools.deserializeBase64(pubKey.getTextContent()); + // read private key from XML + Element privKey = XmlTools.getSingularElement(root, "privatekey"); if (!privKey.getAttribute("encrypted").equals(CryptoTools.getSymmetricAlgorithm())) { throw new MalformedXMLException(CryptoTools.getSymmetricAlgorithm() + " expected"); } - - if (encryptedKeys == null) { - throw new MalformedXMLException("unlockKeys expected"); - } + byte[] encPrivate = Base64.getDecoder().decode(privKey.getTextContent()); + // read member keys from XML + Element encryptedKeys = XmlTools.getSingularElement(root, "unlockKeys"); if (!encryptedKeys.getAttribute("method").equals(CryptoTools.getAsymmetricAlgorithm())) { throw new MalformedXMLException("base64 encoding expected"); } - - long id = Long.parseLong(elId.getFirstChild().getText()); - PublicKey publicKey = (PublicKey) SerializeTools.deserializeBase64(pubKey.getFirstChild().getText()); - byte[] encPrivate = Base64.decodeBase64(privKey.getFirstChild().getText()); - Hashtable htMemberKeys = new Hashtable(); - for (Enumeration enKeys = encryptedKeys.getChildren(); enKeys.hasMoreElements();) { - Element elKey = enKeys.nextElement(); - - if (!elKey.getName().equals("keyentry")) - throw new MalformedXMLException("unlockKeys expected"); - if (!elKey.hasAttribute("forAgent")) + NodeList enGroups = encryptedKeys.getElementsByTagName("keyentry"); + for (int n = 0; n < enGroups.getLength(); n++) { + org.w3c.dom.Node node = enGroups.item(n); + short nodeType = node.getNodeType(); + if (nodeType != org.w3c.dom.Node.ELEMENT_NODE) { + throw new MalformedXMLException( + "Node type (" + nodeType + ") is not type element (" + org.w3c.dom.Node.ELEMENT_NODE + ")"); + } + Element elKey = (Element) node; + if (!elKey.hasAttribute("forAgent")) { throw new MalformedXMLException("forAgent attribute expected"); - if (!elKey.getAttribute("encoding").equals("base64")) + } + if (!elKey.getAttribute("encoding").equals("base64")) { throw new MalformedXMLException("base64 encoding expected"); + } long agentId = Long.parseLong(elKey.getAttribute("forAgent")); - byte[] content = Base64.decodeBase64(elKey.getFirstChild().getText()); + byte[] content = Base64.getDecoder().decode(elKey.getTextContent()); htMemberKeys.put(agentId, content); } - GroupAgent result = new GroupAgent(id, publicKey, encPrivate, htMemberKeys); - // attach optional fields + // read and set optional fields + Element groupname = XmlTools.getOptionalElement(root, "groupname"); if (groupname != null) { - result.name = groupname.getFirstChild().getText(); + result.name = groupname.getTextContent(); } + Element userdata = XmlTools.getOptionalElement(root, "userdata"); if (userdata != null) { - String base64UserData = userdata.getFirstChild().getText(); - result.userData = SerializeTools.deserializeBase64(base64UserData); + result.userData = SerializeTools.deserializeBase64(userdata.getTextContent()); } return result; - } catch (XMLSyntaxException e) { - throw new MalformedXMLException("Error parsing xml string", e); } catch (SerializationException e) { throw new MalformedXMLException("Deserialization problems", e); } catch (L2pSecurityException e) { throw new MalformedXMLException("Security Problems creating an agent from the xml string", e); } - } /** @@ -527,8 +488,8 @@ public static GroupAgent createFromXml(Element root) throws MalformedXMLExceptio * @throws CryptoException * @throws SerializationException */ - public static GroupAgent createGroupAgent(Agent[] members) throws L2pSecurityException, CryptoException, - SerializationException { + public static GroupAgent createGroupAgent(Agent[] members) + throws L2pSecurityException, CryptoException, SerializationException { Random r = new Random(); return new GroupAgent(r.nextLong(), CryptoTools.generateKeyPair(), CryptoTools.generateSymmetricKey(), members); } @@ -568,8 +529,8 @@ public void receiveMessage(Message message, AgentContext context) throws Message L2pLogger.logEvent(Event.SERVICE_ERROR, e1.getMessage()); } if (member == null) { - L2pLogger.logEvent(Event.SERVICE_ERROR, "No agent for group member " + memberId - + " found! Skipping member."); + L2pLogger.logEvent(Event.SERVICE_ERROR, + "No agent for group member " + memberId + " found! Skipping member."); continue; } try { diff --git a/src/main/java/i5/las2peer/security/MonitoringAgent.java b/src/main/java/i5/las2peer/security/MonitoringAgent.java index 1af9be5a5..0d0e611ec 100644 --- a/src/main/java/i5/las2peer/security/MonitoringAgent.java +++ b/src/main/java/i5/las2peer/security/MonitoringAgent.java @@ -3,9 +3,10 @@ import java.io.Serializable; import java.security.KeyPair; import java.security.PublicKey; +import java.util.Base64; import java.util.Random; -import org.apache.commons.codec.binary.Base64; +import org.w3c.dom.Element; import i5.las2peer.communication.Message; import i5.las2peer.communication.MessageException; @@ -18,9 +19,7 @@ import i5.las2peer.tools.CryptoTools; import i5.las2peer.tools.SerializationException; import i5.las2peer.tools.SerializeTools; -import i5.simpleXML.Element; -import i5.simpleXML.Parser; -import i5.simpleXML.XMLSyntaxException; +import i5.las2peer.tools.XmlTools; /** * @@ -145,7 +144,7 @@ public String toXmlString() { + "\n" + "\t" + SerializeTools.serializeToBase64(getPublicKey()) + "\n" + "\t\n" - + "\t\t" + Base64.encodeBase64String(getSalt()) + "\n" + + "\t\t" + Base64.getEncoder().encodeToString(getSalt()) + "\n" + "\t\t" + getEncodedPrivate() + "\n" + "\t\n"); result.append("\n"); @@ -169,79 +168,53 @@ public String toXmlString() { * */ public static MonitoringAgent createFromXml(String xml) throws MalformedXMLException { - try { - Element root = Parser.parse(xml, false); - if (!"monitoring".equals(root.getAttribute("type"))) { - throw new MalformedXMLException("Monitoring agent expected"); - } - if (!"agent".equals(root.getName())) { - throw new MalformedXMLException("Agent expeced"); - } - return createFromXml(root); - } catch (XMLSyntaxException e) { - throw new MalformedXMLException("Error parsing xml string", e); - } + return createFromXml(XmlTools.getRootElement(xml, "las2peer:agent")); } /** * * Sets the state of the object from a string representation resulting from a previous {@link #toXmlString} call. * - * @param root parsed XML document + * @param rootElement parsed XML document * @return * @exception MalformedXMLException * */ - public static MonitoringAgent createFromXml(Element root) throws MalformedXMLException { + public static MonitoringAgent createFromXml(Element rootElement) throws MalformedXMLException { try { - Element elId = root.getFirstChild(); - long id = Long.parseLong(elId.getFirstChild().getText()); - - Element pubKey = root.getChild(1); - if (!pubKey.getName().equals("publickey")) { - throw new MalformedXMLException("public key expected"); - } + // read id from XML + Element elId = XmlTools.getSingularElement(rootElement, "id"); + long id = Long.parseLong(elId.getTextContent()); + // read public key from XML + Element pubKey = XmlTools.getSingularElement(rootElement, "publickey"); if (!pubKey.getAttribute("encoding").equals("base64")) { throw new MalformedXMLException("base64 encoding expected"); } - - PublicKey publicKey = (PublicKey) SerializeTools.deserializeBase64(pubKey.getFirstChild().getText()); - - Element privKey = root.getChild(2); - if (!privKey.getName().equals("privatekey")) { - throw new MalformedXMLException("private key expected"); - } + PublicKey publicKey = (PublicKey) SerializeTools.deserializeBase64(pubKey.getTextContent()); + // read private key from XML + Element privKey = XmlTools.getSingularElement(rootElement, "privatekey"); if (!privKey.getAttribute("encrypted").equals(CryptoTools.getSymmetricAlgorithm())) { throw new MalformedXMLException(CryptoTools.getSymmetricAlgorithm() + " expected"); } if (!privKey.getAttribute("keygen").equals(CryptoTools.getSymmetricKeygenMethod())) { throw new MalformedXMLException(CryptoTools.getSymmetricKeygenMethod() + " expected"); } - - Element elSalt = privKey.getFirstChild(); - if (!elSalt.getName().equals("salt")) { - throw new MalformedXMLException("salt expected"); - } + // read salt from XML + Element elSalt = XmlTools.getSingularElement(rootElement, "salt"); if (!elSalt.getAttribute("encoding").equals("base64")) { throw new MalformedXMLException("base64 encoding expected"); } - - byte[] salt = Base64.decodeBase64(elSalt.getFirstChild().getText()); - - Element data = privKey.getChild(1); - if (!data.getName().equals("data")) { - throw new MalformedXMLException("data expected"); - } + byte[] salt = Base64.getDecoder().decode(elSalt.getTextContent()); + // read data from XML + Element data = XmlTools.getSingularElement(rootElement, "data"); if (!data.getAttribute("encoding").equals("base64")) { throw new MalformedXMLException("base64 encoding expected"); } - byte[] encPrivate = Base64.decodeBase64(data.getFirstChild().getText()); + byte[] encPrivate = Base64.getDecoder().decode(data.getTextContent()); MonitoringAgent result = new MonitoringAgent(id, publicKey, encPrivate, salt); return result; - } catch (XMLSyntaxException e) { - throw new MalformedXMLException("Error parsing XML string", e); } catch (SerializationException e) { throw new MalformedXMLException("Deserialization problems", e); } diff --git a/src/main/java/i5/las2peer/security/ServiceAgent.java b/src/main/java/i5/las2peer/security/ServiceAgent.java index 3ea3dd5e9..185530112 100644 --- a/src/main/java/i5/las2peer/security/ServiceAgent.java +++ b/src/main/java/i5/las2peer/security/ServiceAgent.java @@ -6,9 +6,10 @@ import java.lang.reflect.Method; import java.security.KeyPair; import java.security.PublicKey; +import java.util.Base64; import java.util.Random; -import org.apache.commons.codec.binary.Base64; +import org.w3c.dom.Element; import i5.las2peer.api.Service; import i5.las2peer.classLoaders.ClassLoaderException; @@ -38,9 +39,7 @@ import i5.las2peer.tools.SerializationException; import i5.las2peer.tools.SerializeTools; import i5.las2peer.tools.SimpleTools; -import i5.simpleXML.Element; -import i5.simpleXML.Parser; -import i5.simpleXML.XMLSyntaxException; +import i5.las2peer.tools.XmlTools; /** * A service agent represents a service and its access rights in the las2peer setting. @@ -105,9 +104,9 @@ public String toXmlString() { + "\t" + getId() + "\n" + "\t" + SerializeTools.serializeToBase64(getPublicKey()) + "\n" + "\t\n" + "\t\t" + Base64.encodeBase64String(getSalt()) + "\n" - + "\t\t" + getEncodedPrivate() + "\n" + "\t\n" - + "\n"; + + "\">\n" + "\t\t" + Base64.getEncoder().encodeToString(getSalt()) + + "\n" + "\t\t" + getEncodedPrivate() + "\n" + + "\t\n" + "\n"; } catch (SerializationException e) { throw new RuntimeException("Serialization problems with keys"); } @@ -315,82 +314,55 @@ public static ServiceAgent createServiceAgent(String serviceName, String passphr * @throws MalformedXMLException */ public static ServiceAgent createFromXml(String xml) throws MalformedXMLException { - try { - Element root = Parser.parse(xml, false); - - if (!"service".equals(root.getAttribute("type"))) { - throw new MalformedXMLException("service agent expeced"); - } - if (!"agent".equals(root.getName())) { - throw new MalformedXMLException("agent expected"); - } - - return createFromXml(root); - } catch (XMLSyntaxException e) { - throw new MalformedXMLException("Error parsing xml string", e); - } + return createFromXml(XmlTools.getRootElement(xml, "las2peer:agent")); } /** * factory: create a service agent from the given xml representation * - * @param root + * @param rootElement * @return a service agent * @throws MalformedXMLException */ - public static ServiceAgent createFromXml(Element root) throws MalformedXMLException { + public static ServiceAgent createFromXml(Element rootElement) throws MalformedXMLException { try { - Element elId = root.getFirstChild(); - if (!root.hasAttribute("serviceclass")) { + // read service class from XML + if (!rootElement.hasAttribute("serviceclass")) { throw new MalformedXMLException("serviceclass attribute expected!"); } - ServiceNameVersion service = ServiceNameVersion.fromString(root.getAttribute("serviceclass")); - - long id = Long.parseLong(elId.getFirstChild().getText()); - - Element pubKey = root.getChild(1); - if (!pubKey.getName().equals("publickey")) { - throw new MalformedXMLException("public key expected"); - } + ServiceNameVersion service = ServiceNameVersion.fromString(rootElement.getAttribute("serviceclass")); + // read id from XML + Element elId = XmlTools.getSingularElement(rootElement, "id"); + long id = Long.parseLong(elId.getTextContent()); + // read public key from XML + Element pubKey = XmlTools.getSingularElement(rootElement, "publickey"); if (!pubKey.getAttribute("encoding").equals("base64")) { throw new MalformedXMLException("base64 encoding expected"); } - - PublicKey publicKey = (PublicKey) SerializeTools.deserializeBase64(pubKey.getFirstChild().getText()); - - Element privKey = root.getChild(2); - if (!privKey.getName().equals("privatekey")) { - throw new MalformedXMLException("private key expected"); - } + PublicKey publicKey = (PublicKey) SerializeTools.deserializeBase64(pubKey.getTextContent()); + // read private key from XML + Element privKey = XmlTools.getSingularElement(rootElement, "privatekey"); if (!privKey.getAttribute("encrypted").equals(CryptoTools.getSymmetricAlgorithm())) { throw new MalformedXMLException(CryptoTools.getSymmetricAlgorithm() + " expected"); } if (!privKey.getAttribute("keygen").equals(CryptoTools.getSymmetricKeygenMethod())) { throw new MalformedXMLException(CryptoTools.getSymmetricKeygenMethod() + " expected"); } - - Element elSalt = privKey.getFirstChild(); - if (!elSalt.getName().equals("salt")) { - throw new MalformedXMLException("salt expected"); - } + // read salt from XML + Element elSalt = XmlTools.getSingularElement(rootElement, "salt"); if (!elSalt.getAttribute("encoding").equals("base64")) { throw new MalformedXMLException("base64 encoding expected"); } - byte[] salt = Base64.decodeBase64(elSalt.getFirstChild().getText()); - - Element data = privKey.getChild(1); - if (!data.getName().equals("data")) { - throw new MalformedXMLException("data expected"); - } + byte[] salt = Base64.getDecoder().decode(elSalt.getTextContent()); + // read data from XML + Element data = XmlTools.getSingularElement(rootElement, "data"); if (!data.getAttribute("encoding").equals("base64")) { throw new MalformedXMLException("base64 encoding expected"); } - byte[] encPrivate = Base64.decodeBase64(data.getFirstChild().getText()); + byte[] encPrivate = Base64.getDecoder().decode(data.getTextContent()); return new ServiceAgent(id, service, publicKey, encPrivate, salt); - } catch (XMLSyntaxException e) { - throw new MalformedXMLException("Error parsing xml string", e); } catch (SerializationException e) { throw new MalformedXMLException("Deserialization problems", e); } diff --git a/src/main/java/i5/las2peer/security/UserAgent.java b/src/main/java/i5/las2peer/security/UserAgent.java index c4e5916e4..0cf22b5b5 100644 --- a/src/main/java/i5/las2peer/security/UserAgent.java +++ b/src/main/java/i5/las2peer/security/UserAgent.java @@ -3,10 +3,11 @@ import java.io.Serializable; import java.security.KeyPair; import java.security.PublicKey; +import java.util.Base64; import java.util.Random; import java.util.regex.Pattern; -import org.apache.commons.codec.binary.Base64; +import org.w3c.dom.Element; import i5.las2peer.communication.Message; import i5.las2peer.communication.MessageException; @@ -19,9 +20,7 @@ import i5.las2peer.tools.CryptoTools; import i5.las2peer.tools.SerializationException; import i5.las2peer.tools.SerializeTools; -import i5.simpleXML.Element; -import i5.simpleXML.Parser; -import i5.simpleXML.XMLSyntaxException; +import i5.las2peer.tools.XmlTools; /** * An UserAgent represent a (End)user of the las2peer system. @@ -151,7 +150,7 @@ public String toXmlString() { + "\t" + SerializeTools.serializeToBase64(getPublicKey()) + "\n" + "\t\n" - + "\t\t" + Base64.encodeBase64String(getSalt()) + "\n" + + "\t\t" + Base64.getEncoder().encodeToString(getSalt()) + "\n" + "\t\t" + getEncodedPrivate() + "\n" + "\t\n"); if (sLoginName != null) { @@ -161,7 +160,8 @@ public String toXmlString() { result.append("\t" + sEmail + "\n"); } if (userData != null) { - result.append("\t" + SerializeTools.serializeToBase64(userData) + "\n"); + result.append("\t" + SerializeTools.serializeToBase64(userData) + + "\n"); } result.append("\n"); @@ -186,20 +186,7 @@ public String toXmlString() { * */ public static UserAgent createFromXml(String xml) throws MalformedXMLException { - try { - Element root = Parser.parse(xml, false); - - if (!"user".equals(root.getAttribute("type"))) { - throw new MalformedXMLException("user agent expected"); - } - - if (!"agent".equals(root.getName())) { - throw new MalformedXMLException("agent expected"); - } - return createFromXml(root); - } catch (XMLSyntaxException e) { - throw new MalformedXMLException("Error parsing xml string", e); - } + return createFromXml(XmlTools.getRootElement(xml, "las2peer:agent")); } /** @@ -237,7 +224,7 @@ public static UserAgent createUserAgent(long id, String passphrase) throws Crypt /** * Sets the state of the object from a string representation resulting from a previous {@link #toXmlString} call. * - * @param root parsed xml document + * @param root parsed XML document * @return * * @exception MalformedXMLException @@ -245,77 +232,58 @@ public static UserAgent createUserAgent(long id, String passphrase) throws Crypt */ public static UserAgent createFromXml(Element root) throws MalformedXMLException { try { - Element elId = root.getFirstChild(); - long id = Long.parseLong(elId.getFirstChild().getText()); - - Element pubKey = root.getChild(1); - if (!pubKey.getName().equals("publickey")) { - throw new MalformedXMLException("public key expected"); - } + // read id field from XML + Element elId = XmlTools.getSingularElement(root, "id"); + long id = Long.parseLong(elId.getTextContent()); + // read public key from XML + Element pubKey = XmlTools.getSingularElement(root, "publickey"); if (!pubKey.getAttribute("encoding").equals("base64")) { throw new MalformedXMLException("base64 encoding expected"); } - - PublicKey publicKey = (PublicKey) SerializeTools.deserializeBase64(pubKey.getFirstChild().getText()); - - Element privKey = root.getChild(2); - if (!privKey.getName().equals("privatekey")) { - throw new MalformedXMLException("private key expected"); - } + PublicKey publicKey = (PublicKey) SerializeTools.deserializeBase64(pubKey.getTextContent()); + // read private key from XML + Element privKey = XmlTools.getSingularElement(root, "privatekey"); if (!privKey.getAttribute("encrypted").equals(CryptoTools.getSymmetricAlgorithm())) { throw new MalformedXMLException(CryptoTools.getSymmetricAlgorithm() + " expected"); } if (!privKey.getAttribute("keygen").equals(CryptoTools.getSymmetricKeygenMethod())) { throw new MalformedXMLException(CryptoTools.getSymmetricKeygenMethod() + " expected"); } - - Element elSalt = privKey.getFirstChild(); - if (!elSalt.getName().equals("salt")) { - throw new MalformedXMLException("salt expected"); - } + Element dataPrivate = XmlTools.getSingularElement(privKey, "data"); + byte[] encPrivate = Base64.getDecoder().decode(dataPrivate.getTextContent()); + // read salt from XML + Element elSalt = XmlTools.getSingularElement(root, "salt"); if (!elSalt.getAttribute("encoding").equals("base64")) { throw new MalformedXMLException("base64 encoding expected"); } + byte[] salt = Base64.getDecoder().decode(elSalt.getTextContent()); - byte[] salt = Base64.decodeBase64(elSalt.getFirstChild().getText()); - - Element data = privKey.getChild(1); - if (!data.getName().equals("data")) { - throw new MalformedXMLException("data expected"); - } - if (!data.getAttribute("encoding").equals("base64")) { - throw new MalformedXMLException("base64 encoding expected"); - } - byte[] encPrivate = Base64.decodeBase64(data.getFirstChild().getText()); - + // required fields complete, create result UserAgent result = new UserAgent(id, publicKey, encPrivate, salt); - int cnt = 3; - Element login = root.getChild(cnt); - if (login != null && login.getName().equals("login")) { - result.sLoginName = login.getFirstChild().getText(); - cnt++; - } + // read and set optional fields - Element email = root.getChild(cnt); + // optional login name + Element login = XmlTools.getOptionalElement(root, "login"); + if (login != null) { + result.sLoginName = login.getTextContent(); + } + // optional email address + Element email = XmlTools.getOptionalElement(root, "email"); if (email != null) { - if (!email.getName().equals("email")) { - throw new MalformedXMLException("email or login element expected!"); - } - result.sEmail = email.getFirstChild().getText(); - cnt++; + result.sEmail = email.getTextContent(); } - - Element xmlUserData = root.getChild(cnt); - if (xmlUserData != null && xmlUserData.getName().equals("userdata")) { - String base64UserData = xmlUserData.getFirstChild().getText(); - result.userData = SerializeTools.deserializeBase64(base64UserData); - cnt++; + // optional user data + Element userdata = XmlTools.getOptionalElement(root, "userdata"); + if (userdata != null) { + if (userdata.hasAttribute("encoding") + && !userdata.getAttribute("encoding").equalsIgnoreCase("base64")) { + throw new MalformedXMLException("base64 encoding expected"); + } + result.userData = SerializeTools.deserializeBase64(userdata.getTextContent()); } return result; - } catch (XMLSyntaxException e) { - throw new MalformedXMLException("Error parsing xml string", e); } catch (SerializationException e) { throw new MalformedXMLException("Deserialization problems", e); } diff --git a/src/main/java/i5/las2peer/testing/TestSuite.java b/src/main/java/i5/las2peer/testing/TestSuite.java index 2192f0541..703345945 100644 --- a/src/main/java/i5/las2peer/testing/TestSuite.java +++ b/src/main/java/i5/las2peer/testing/TestSuite.java @@ -62,9 +62,9 @@ public static ArrayList launchNetwork(int numOfNodes, STORAGE_MO System.out.println("bootstrap node launched with id " + bootstrapNode.getNodeId()); // add more nodes for (int num = 1; num <= numOfNodes - 1; num++) { - PastryNodeImpl node2 = addNode(bootstrapPort, storageMode, (long) num); - result.add(node2); - System.out.println("network node launched with id " + bootstrapNode.getNodeId()); + PastryNodeImpl node = addNode(bootstrapPort, storageMode, (long) num); + result.add(node); + System.out.println("network node launched with id " + node.getNodeId()); } return result; } diff --git a/src/main/java/i5/las2peer/tools/CommandPrompt.java b/src/main/java/i5/las2peer/tools/CommandPrompt.java index ee0341800..12c60c4fb 100644 --- a/src/main/java/i5/las2peer/tools/CommandPrompt.java +++ b/src/main/java/i5/las2peer/tools/CommandPrompt.java @@ -600,6 +600,9 @@ else if (split.length > 2) { } else if (split[0].equals("?") || split[0].equals("help")) { printHelp(); return ReturnStatus.OK_PROCEED; + } else if (split[0].equals("v") || split[0].equals("version")) { + printVersion(); + return ReturnStatus.OK_PROCEED; } return ReturnStatus.NOT_KNOWN_PROCEED; @@ -697,6 +700,8 @@ public void printHelp() { + "\thelp / ?\tprint this message\n\n" + + "\tversion / ?\tv returns the las2peer version running on this node.\n\n" + + "\tp(rint) [var1] [var2] ...\n" + "\t\t\tprint the contents of the given local variables\n\n" + "\tpackage\t[some.package.name]\n\n" @@ -723,6 +728,15 @@ public void printHelp() { + "Lines statring with // or # will be ignored\n\n"); } + /** + * Prints the las2peer version. + */ + public void printVersion() { + Package p = L2pNodeLauncher.class.getPackage(); + String version = p.getImplementationVersion(); + System.out.println("las2peer version \"" + version + "\""); + } + /** * print a simple prompt and wait for input * diff --git a/src/main/java/i5/las2peer/tools/L2pNodeLauncher.java b/src/main/java/i5/las2peer/tools/L2pNodeLauncher.java index 720149c28..730f08212 100644 --- a/src/main/java/i5/las2peer/tools/L2pNodeLauncher.java +++ b/src/main/java/i5/las2peer/tools/L2pNodeLauncher.java @@ -1,5 +1,24 @@ package i5.las2peer.tools; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.Serializable; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + import i5.las2peer.api.Connector; import i5.las2peer.api.ConnectorException; import i5.las2peer.api.exceptions.ArtifactNotFoundException; @@ -19,7 +38,6 @@ import i5.las2peer.p2p.ServiceNameVersion; import i5.las2peer.p2p.TimeoutException; import i5.las2peer.persistency.EncodingFailedException; -import i5.las2peer.persistency.Envelope; import i5.las2peer.persistency.MalformedXMLException; import i5.las2peer.persistency.SharedStorage.STORAGE_MODE; import i5.las2peer.security.Agent; @@ -30,27 +48,6 @@ import i5.las2peer.security.PassphraseAgent; import i5.las2peer.security.ServiceAgent; import i5.las2peer.security.UserAgent; -import i5.simpleXML.Element; -import i5.simpleXML.Parser; -import i5.simpleXML.XMLSyntaxException; - -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.Serializable; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - import rice.p2p.commonapi.NodeHandle; /** @@ -99,8 +96,8 @@ public PastryNodeImpl getNode() { * @throws NumberFormatException * @throws SerializationException */ - public String getEnvelope(String id) throws NumberFormatException, ArtifactNotFoundException, StorageException, - SerializationException { + public String getEnvelope(String id) + throws NumberFormatException, ArtifactNotFoundException, StorageException, SerializationException { return node.fetchEnvelope(id).toXmlString(); } @@ -204,48 +201,32 @@ public boolean accept(File dir, String name) { } })) { try { - Element xmlRoot = Parser.parse(xmlFile, false); - if (xmlRoot.getName().equalsIgnoreCase(Agent.class.getSimpleName())) { - Agent agent = Agent.createFromXml(xmlRoot); - agentIdToXml.put(agent.getId(), xmlFile.getName()); - if (agent instanceof PassphraseAgent) { - String passphrase = htPassphrases.get(xmlFile.getName()); - if (passphrase != null) { - ((PassphraseAgent) agent).unlockPrivateKey(passphrase); - } else { - printWarning("\t- got no passphrase for agent from " + xmlFile.getName()); - } - node.storeAgent(agent); - printMessage("\t- stored agent from " + xmlFile); - } else if (agent instanceof GroupAgent) { - GroupAgent ga = (GroupAgent) agent; - groupAgents.add(ga); + // maybe an agent? + Agent agent = Agent.createFromXml(xmlFile); + agentIdToXml.put(agent.getId(), xmlFile.getName()); + if (agent instanceof PassphraseAgent) { + String passphrase = htPassphrases.get(xmlFile.getName()); + if (passphrase != null) { + ((PassphraseAgent) agent).unlockPrivateKey(passphrase); } else { - throw new IllegalArgumentException("Unknown agent type: " + agent.getClass()); + printWarning("\t- got no passphrase for agent from " + xmlFile.getName()); } - } else if (xmlRoot.getName().equalsIgnoreCase(Envelope.class.getSimpleName())) { - Envelope e = Envelope.createFromXml(xmlRoot); - // TODO fix upload Envelope from startup directory - node.storeArtifact(e); - printMessage("\t- stored artifact from " + xmlFile); + node.storeAgent(agent); + printMessage("\t- stored agent from " + xmlFile); + } else if (agent instanceof GroupAgent) { + GroupAgent ga = (GroupAgent) agent; + groupAgents.add(ga); } else { - printWarning("Ingoring unknown XML object (" + xmlRoot.getName() + ") in '" + xmlFile.toString() - + "'!"); + throw new IllegalArgumentException("Unknown agent type: " + agent.getClass()); } - } catch (XMLSyntaxException e1) { - printError("Unable to parse XML contents of '" + xmlFile.toString() + "'!"); } catch (MalformedXMLException e) { printError("unable to deserialize contents of " + xmlFile.toString() + "!"); - } catch (IOException e) { - printError("problems reading the contents of " + xmlFile.toString() + ": " + e); } catch (L2pSecurityException e) { printError("error storing agent from " + xmlFile.toString() + ": " + e); } catch (AgentAlreadyRegisteredException e) { printError("agent from " + xmlFile.toString() + " already known at this node!"); } catch (AgentException e) { printError("unable to generate agent " + xmlFile.toString() + "!"); - } catch (StorageException e) { - printError("unable to store contents of " + xmlFile.toString() + "!"); } } // wait till all user agents are added from startup directory to unlock group agents @@ -388,8 +369,8 @@ public void stopConnector(String connectorClass) { * @throws InstantiationException * @throws IllegalAccessException */ - private Connector loadConnector(String classname) throws ClassNotFoundException, InstantiationException, - IllegalAccessException { + private Connector loadConnector(String classname) + throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class connectorClass = L2pNodeLauncher.class.getClassLoader().loadClass(classname); Connector connector = (Connector) connectorClass.newInstance(); return connector; @@ -433,8 +414,8 @@ public boolean registerUserAgent(String id, String passphrase) { * @throws AgentAlreadyRegisteredException * @throws AgentException */ - public void registerUserAgent(UserAgent agent) throws L2pSecurityException, AgentAlreadyRegisteredException, - AgentException { + public void registerUserAgent(UserAgent agent) + throws L2pSecurityException, AgentAlreadyRegisteredException, AgentException { registerUserAgent(agent, null); } @@ -539,9 +520,9 @@ private Serializable invoke(String serviceIdentifier, String serviceMethod, Seri * @throws EncodingFailedException * @throws TimeoutException */ - public ListMethodsContent getServiceMethods(String serviceNameVersion) throws L2pSecurityException, - AgentNotKnownException, InterruptedException, EncodingFailedException, SerializationException, - TimeoutException { + public ListMethodsContent getServiceMethods(String serviceNameVersion) + throws L2pSecurityException, AgentNotKnownException, InterruptedException, EncodingFailedException, + SerializationException, TimeoutException { if (currentUser == null) { throw new IllegalStateException("please log in a valid user with registerUserAgent before!"); } @@ -598,8 +579,13 @@ public void startService(String serviceNameVersion, String defaultPass) throws E Files.write(file.toPath(), a.toXmlString().getBytes()); // save passphrase - Files.write(Paths.get(DEFAULT_SERVICE_AGENT_DIRECTORY + "passphrases.txt"), ("\n" + serviceNameVersion - + ".xml;" + defaultPass).getBytes(), StandardOpenOption.CREATE, StandardOpenOption.APPEND); + Path passphrasesPath = Paths.get(DEFAULT_SERVICE_AGENT_DIRECTORY + "passphrases.txt"); + String passphraseLine = serviceNameVersion + ".xml;" + defaultPass; + try { + Files.write(passphrasesPath, ("\n" + passphraseLine).getBytes(), StandardOpenOption.APPEND); + } catch (NoSuchFileException e) { + Files.write(passphrasesPath, passphraseLine.getBytes(), StandardOpenOption.CREATE); + } } // get passphrase from file @@ -645,8 +631,8 @@ public ServiceAgent startServiceXml(String file, String passphrase) throws Excep * @throws L2pSecurityException * @throws AgentException */ - public void startService(Agent serviceAgent) throws AgentAlreadyRegisteredException, L2pSecurityException, - AgentException { + public void startService(Agent serviceAgent) + throws AgentAlreadyRegisteredException, L2pSecurityException, AgentException { if (!(serviceAgent instanceof ServiceAgent)) { throw new IllegalArgumentException("given Agent is not a service agent!"); @@ -668,8 +654,8 @@ public void startService(Agent serviceAgent) throws AgentAlreadyRegisteredExcept * @throws NoSuchServiceException * @throws NodeException */ - public void stopService(String serviceNameVersion) throws AgentNotKnownException, NoSuchServiceException, - NodeException { + public void stopService(String serviceNameVersion) + throws AgentNotKnownException, NoSuchServiceException, NodeException { ServiceAgent agent = node.getLocalServiceAgent(ServiceNameVersion.fromString(serviceNameVersion)); node.unregisterReceiver(agent); } @@ -706,9 +692,8 @@ public void unlockAgent(PassphraseAgent agent, String passphrase) throws L2pSecu * start interactive console mode based on a {@link i5.las2peer.tools.CommandPrompt} */ public void interactive() { - System.out.println("Entering interactive mode for node " - + this.getNode().getPastryNode().getId().toStringFull() + "\n" - + "-----------------------------------------------\n" + System.out.println("Entering interactive mode for node " + this.getNode().getPastryNode().getId().toStringFull() + + "\n" + "-----------------------------------------------\n" + "Enter 'help' for further information of the console.\n" + "Use all public methods of the L2pNodeLauncher class for interaction with the P2P network.\n\n"); @@ -760,6 +745,11 @@ private L2pNodeLauncher(int port, String bootstrap, STORAGE_MODE storageMode, bo commandPrompt = new CommandPrompt(this); } + private L2pNodeLauncher(L2pClassManager cl, Integer port, String bootstrap) { + node = new PastryNodeImpl(cl, port, bootstrap, STORAGE_MODE.MEMORY, null, null); + commandPrompt = new CommandPrompt(this); + } + /** * actually start the node * @@ -807,6 +797,7 @@ private static void printError(String message) { * @throws NodeException */ public static L2pNodeLauncher launchSingle(Iterable args) throws NodeException { + boolean debugMode = false; Integer port = null; String bootstrap = null; STORAGE_MODE storageMode = null; @@ -821,7 +812,9 @@ public static L2pNodeLauncher launchSingle(Iterable args) throws NodeExc String arg = itArg.next(); itArg.remove(); String larg = arg.toLowerCase(); - if (larg.equals("-p") == true || larg.equals("--port") == true) { + if (larg.equals("-debug") || larg.equals("--debug")) { + debugMode = true; + } else if (larg.equals("-p") == true || larg.equals("--port") == true) { if (itArg.hasNext() == false) { printWarning("ignored '" + arg + "', because port number expected after it"); } else { @@ -902,15 +895,21 @@ public static L2pNodeLauncher launchSingle(Iterable args) throws NodeExc commands.add(arg); } } - // check parameters - if (port == null) { - printError("no port number specified"); - return null; - } else if (port < 1) { - printError("invalid port number specified"); - return null; + // check parameters and launch node + if (debugMode) { + System.err.println("WARNING! Launching node in DEBUG mode! THIS NODE IS NON PERSISTENT!"); + return launchDebug(port, bootstrap, sLogDir, serviceDirectories, commands); + } else { + if (port == null) { + printError("no port number specified"); + return null; + } else if (port < 1) { + printError("invalid port number specified"); + return null; + } + return launchSingle(port, bootstrap, storageMode, observer, sLogDir, serviceDirectories, nodeIdSeed, + commands); } - return launchSingle(port, bootstrap, storageMode, observer, sLogDir, serviceDirectories, nodeIdSeed, commands); } public static L2pNodeLauncher launchSingle(int port, String bootstrap, STORAGE_MODE storageMode, boolean observer, @@ -959,6 +958,51 @@ public static L2pNodeLauncher launchSingle(int port, String bootstrap, STORAGE_M return launcher; } + private static L2pNodeLauncher launchDebug(Integer port, String boostrap, String sLogDir, + Iterable serviceDirectories, Iterable commands) throws NodeException { + // check parameters + if (sLogDir != null) { + try { + L2pLogger.setGlobalLogDirectory(sLogDir); + } catch (Exception ex) { + printWarning("couldn't use '" + sLogDir + "' as log directory." + ex); + } + } + if (serviceDirectories == null) { + ArrayList directories = new ArrayList<>(); + directories.add(DEFAULT_SERVICE_DIRECTORY); + serviceDirectories = directories; + } + if (commands == null) { + commands = new ArrayList<>(); + } + // instantiate launcher + L2pClassManager cl = new L2pClassManager(new FileSystemRepository(serviceDirectories, true), + L2pNodeLauncher.class.getClassLoader()); + L2pNodeLauncher launcher = new L2pNodeLauncher(cl, port, boostrap); + // handle commands + try { + launcher.start(); + + for (String command : commands) { + System.out.println("Handling: '" + command + "'"); + launcher.commandPrompt.handleLine(command); + } + + if (launcher.isFinished()) { + printMessage("All commands have been handled and shutdown has been called -> end!"); + } else { + printMessage("All commands have been handled -- keeping node open!"); + } + } catch (NodeException e) { + launcher.bFinished = true; + logger.printStackTrace(e); + throw e; + } + + return launcher; + } + /** * Prints a help message for command line usage. * @@ -976,6 +1020,9 @@ public static void printHelp(String message) { System.out.println("Help Message:"); System.out.println("\t['--help'|'-h']"); + System.out.println("las2peer version:"); + System.out.println("\t['--version'|'-v']"); + System.out.println("\nStart Node:"); System.out .println("\t{optional: --colored-shell|-c} -p [port] {optional1} {optional2} {method1} {method2} ..."); @@ -991,11 +1038,11 @@ public static void printHelp(String message) { System.out.println("\t--port|-p [port] specifies the port number of the node\n"); System.out.println("\tno bootstrap argument states, that a complete new las2peer network is to start"); System.out.println("\tor"); - System.out - .println("\t--bootstrap|-b [host-list] requires a comma seperated list of [address:ip] pairs of bootstrap nodes to connect to. This argument can occur multiple times.\n"); + System.out.println( + "\t--bootstrap|-b [host-list] requires a comma seperated list of [address:ip] pairs of bootstrap nodes to connect to. This argument can occur multiple times.\n"); System.out.println("\t--observer|-o starts a monitoring observer at this node\n"); - System.out - .println("\t--node-id-seed|-n [long] generates the node id by using this seed to provide persistence\n"); + System.out.println( + "\t--node-id-seed|-n [long] generates the node id by using this seed to provide persistence\n"); System.out .println("\t--storage-mode|-m filesystem|memory sets Pastry's storage mode, defaults to filesystem\n"); @@ -1019,6 +1066,15 @@ public static void printHelp() { printHelp(null); } + /** + * Prints the las2peer version. + */ + public static void printVersion() { + Package p = L2pNodeLauncher.class.getPackage(); + String version = p.getImplementationVersion(); + System.out.println("las2peer version \"" + version + "\""); + } + /** * Main method for command line processing. * @@ -1082,8 +1138,12 @@ public static void main(String[] argv) throws InterruptedException, MalformedXML if (larg.equals("-h") == true || larg.equals("--help") == true) { // Help Message printHelp(); System.exit(1); + } else if (larg.equals("-v") || larg.equals("--version")) { + printVersion(); + System.exit(1); } else if (larg.equals("-w") || larg.equals("--windows-shell")) { - printWarning("Ignoring obsolete argument '" + arg + ", because colored output is disabled by default."); + printWarning( + "Ignoring obsolete argument '" + arg + "', because colored output is disabled by default."); } else if (larg.equals("-c") == true || larg.equals("--colored-shell") == true) { // turn on colored output ColoredOutput.allOn(); } else { // node instance parameter diff --git a/src/main/java/i5/las2peer/tools/SerializeTools.java b/src/main/java/i5/las2peer/tools/SerializeTools.java index 2158c0426..30c9807d4 100755 --- a/src/main/java/i5/las2peer/tools/SerializeTools.java +++ b/src/main/java/i5/las2peer/tools/SerializeTools.java @@ -7,11 +7,10 @@ import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.io.Serializable; +import java.util.Base64; import javax.crypto.SecretKey; -import org.apache.commons.codec.binary.Base64; - /** * Static class as collection of serialization and deserialization methods. * @@ -47,7 +46,7 @@ public static byte[] serialize(Serializable s) throws SerializationException { * @throws SerializationException */ public static String serializeToBase64(Serializable s) throws SerializationException { - return Base64.encodeBase64String(serialize(s)); + return Base64.getEncoder().encodeToString(serialize(s)); } /** @@ -103,7 +102,7 @@ protected Class resolveClass(ObjectStreamClass desc) throws IOException, Clas * @throws SerializationException */ public static Serializable deserializeBase64(String base64) throws SerializationException { - return deserialize(Base64.decodeBase64(base64)); + return deserialize(Base64.getDecoder().decode(base64)); } /** diff --git a/src/main/java/i5/las2peer/tools/XmlTools.java b/src/main/java/i5/las2peer/tools/XmlTools.java index bcbe63e0c..85fa994dc 100644 --- a/src/main/java/i5/las2peer/tools/XmlTools.java +++ b/src/main/java/i5/las2peer/tools/XmlTools.java @@ -1,8 +1,21 @@ package i5.las2peer.tools; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.nio.charset.StandardCharsets; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; import i5.las2peer.persistency.MalformedXMLException; import i5.las2peer.persistency.XmlAble; @@ -44,21 +57,24 @@ public static XmlAble createFromXml(String xml, String classname) public static XmlAble createFromXml(String xml, Class c) throws MalformedXMLException, SerializationException { try { Method createFromXml = c.getDeclaredMethod("createFromXml", String.class); - if (Modifier.isStatic(createFromXml.getModifiers())) + if (Modifier.isStatic(createFromXml.getModifiers())) { return (XmlAble) createFromXml.invoke(null, xml); + } } catch (Exception e1) { - if (e1 instanceof MalformedXMLException) + if (e1 instanceof MalformedXMLException) { throw (MalformedXMLException) e1; - // just try next idea + // just try next idea + } } try { Constructor constr = c.getDeclaredConstructor(String.class); return (XmlAble) constr.newInstance(xml); } catch (Exception e) { - if (e instanceof MalformedXMLException) + if (e instanceof MalformedXMLException) { throw (MalformedXMLException) e; - // again just the next one + // again just the next one + } } try { @@ -68,11 +84,132 @@ public static XmlAble createFromXml(String xml, Class c) throws MalformedXMLE return result; } catch (Exception e) { - if (e instanceof MalformedXMLException) + if (e instanceof MalformedXMLException) { throw (MalformedXMLException) e; + } } throw new SerializationException("unable to generate a new instance for the given xml: " + xml); } + /** + * Gets the root element from the given XML String and throws an exception if the name does not match with the given + * name. + * + * @param xml The XML String that should be parsed. + * @param rootElementName The tag name of the root element. CASE SENSITIVE + * @return Returns the root element with the given tag name. + * @throws MalformedXMLException If the root element does not have the given name or multiple root elements exist. + */ + public static Element getRootElement(String xml, String rootElementName) throws MalformedXMLException { + try { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document doc = dBuilder.parse(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))); + return getRootElement(doc, rootElementName); + } catch (ParserConfigurationException | IOException | SAXException e) { + throw new MalformedXMLException("Error parsing xml string", e); + } + } + + /** + * Gets the root element from the given file containing only ONE XML representation and throws an exception if the + * name does not match with the given name. + * + * @param xmlFile The file containing one XML representation that should be parsed. + * @param rootElementName The tag name of the root element. CASE SENSITIVE + * @return Returns the root element with the given tag name. + * @throws MalformedXMLException If the root element does not have the given name or multiple root elements exist. + */ + public static Element getRootElement(File xmlFile, String rootElementName) throws MalformedXMLException { + try { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document doc = dBuilder.parse(xmlFile); + return getRootElement(doc, rootElementName); + } catch (ParserConfigurationException | IOException | SAXException e) { + throw new MalformedXMLException("Error parsing xml string", e); + } + } + + /** + * Gets the root element from the given document type containing only ONE XML representation and throws an exception + * if the name does not match with the given name. + * + * @param document The document type containing one XML representation that should be parsed. + * @param rootElementName The tag name of the root element. CASE SENSITIVE + * @return Returns the root element with the given tag name. + * @throws MalformedXMLException If the root element does not have the given name or multiple root elements exist. + */ + public static Element getRootElement(Document document, String rootElementName) throws MalformedXMLException { + document.getDocumentElement().normalize(); + NodeList nList = document.getElementsByTagName(rootElementName); + int len = nList.getLength(); + if (len != 1) { + throw new MalformedXMLException( + "Exactly one element '" + rootElementName + "' per XML document expected! Found: " + len); + } + org.w3c.dom.Node rootNode = nList.item(0); + String rootNodeName = rootNode.getNodeName(); + if (!rootNodeName.equalsIgnoreCase(rootElementName)) { + throw new MalformedXMLException("This is not an " + rootElementName + " but a " + rootNodeName); + } + short rootNodeType = rootNode.getNodeType(); + if (rootNodeType != org.w3c.dom.Node.ELEMENT_NODE) { + throw new MalformedXMLException("Root node type (" + rootNodeType + ") is not type element (" + + org.w3c.dom.Node.ELEMENT_NODE + ")"); + } + return (Element) rootNode; + } + + /** + * Gets exactly one child tag from given parent XML element. Otherwise throws an exception. + * + * @param parent The parent XML element. + * @param tagName The tag name of the singular child element. CASE SENSITIVE + * @return Returns the child element with the given tag name. + * @throws MalformedXMLException If not exactly one child has the specified tag name or it's not a node itself. + */ + public static Element getSingularElement(Element parent, String tagName) throws MalformedXMLException { + NodeList nodeList = parent.getElementsByTagName(tagName); + int len = nodeList.getLength(); + if (len != 1) { + throw new MalformedXMLException("Exactly one '" + tagName + "' element expected! Found: " + len); + } + org.w3c.dom.Node firstNode = nodeList.item(0); + short nodeType = firstNode.getNodeType(); + if (nodeType != org.w3c.dom.Node.ELEMENT_NODE) { + throw new MalformedXMLException( + "Node type (" + nodeType + ") is not type element (" + org.w3c.dom.Node.ELEMENT_NODE + ")"); + } + return (Element) firstNode; + } + + /** + * Gets one optional child tag from given parent XML element. If more than one child matches an exception is thrown. + * + * @param parent The parent XML element. + * @param tagName The tag name of the optional child element. CASE SENSITIVE + * @return Returns the child element with the given tag name or null if no child matches the given tag name. + * @throws MalformedXMLException If more than one child has the specified tag name or it's not a node itself. + */ + public static Element getOptionalElement(Element parent, String tagName) throws MalformedXMLException { + NodeList nodeList = parent.getElementsByTagName(tagName); + int len = nodeList.getLength(); + if (len > 1) { + throw new MalformedXMLException("Only one '" + tagName + "' element expected!"); + } else if (len == 1) { + org.w3c.dom.Node firstNode = nodeList.item(0); + short nodeType = firstNode.getNodeType(); + if (nodeType != org.w3c.dom.Node.ELEMENT_NODE) { + throw new MalformedXMLException( + "Node type (" + nodeType + ") is not type element (" + org.w3c.dom.Node.ELEMENT_NODE + ")"); + } + return (Element) firstNode; + } else { + // no child element with tag name found + return null; + } + } + } diff --git a/src/test/java/i5/las2peer/p2p/NodeInformationTest.java b/src/test/java/i5/las2peer/p2p/NodeInformationTest.java index 1ead825d7..2a5d286c9 100644 --- a/src/test/java/i5/las2peer/p2p/NodeInformationTest.java +++ b/src/test/java/i5/las2peer/p2p/NodeInformationTest.java @@ -6,12 +6,11 @@ import org.junit.Test; import i5.las2peer.persistency.MalformedXMLException; -import i5.simpleXML.XMLSyntaxException; public class NodeInformationTest { @Test - public void testXmlAndBack() throws XMLSyntaxException, MalformedXMLException { + public void testXmlAndBack() throws MalformedXMLException { NodeInformation testee = NodeInformation.createFromXml( "test@bla.comStevensome desc"); diff --git a/src/test/java/i5/las2peer/p2p/NodeServiceCacheTest.java b/src/test/java/i5/las2peer/p2p/NodeServiceCacheTest.java index 894995b6b..51838ae2b 100644 --- a/src/test/java/i5/las2peer/p2p/NodeServiceCacheTest.java +++ b/src/test/java/i5/las2peer/p2p/NodeServiceCacheTest.java @@ -102,7 +102,7 @@ public void testIntegration() throws CryptoException, L2pSecurityException, Agen @Test public void testGlobalServices() throws CryptoException, L2pSecurityException, AgentAlreadyRegisteredException, AgentException, CloneNotSupportedException, MalformedXMLException, IOException, NodeException { - // Attention when chaning NodeServiceCache parameters + // Attention when changing NodeServiceCache parameters // You may have to adjust these results afterwards since this may influence the selected versions // launch nodes diff --git a/src/test/java/i5/las2peer/persistency/AgentUpdateTest.java b/src/test/java/i5/las2peer/persistency/AgentUpdateTest.java new file mode 100644 index 000000000..9f7d611a3 --- /dev/null +++ b/src/test/java/i5/las2peer/persistency/AgentUpdateTest.java @@ -0,0 +1,61 @@ +package i5.las2peer.persistency; + +import java.util.ArrayList; + +import org.junit.Assert; +import org.junit.Test; + +import i5.las2peer.p2p.PastryNodeImpl; +import i5.las2peer.security.Agent; +import i5.las2peer.security.GroupAgent; +import i5.las2peer.security.UserAgent; +import i5.las2peer.testing.MockAgentFactory; +import i5.las2peer.testing.TestSuite; + +public class AgentUpdateTest { + + /** + * This test launches a network with two nodes. The first node creates an GroupAgent and modifies it. The second + * node verifies that all changes are correctly stored in the network. + */ + @Test + public void testGroupAgentUpdate() { + try { + // launch network + ArrayList nodes = TestSuite.launchNetwork(2); + PastryNodeImpl firstNode = nodes.get(0); + // create agents + UserAgent smith = MockAgentFactory.getAdam(); + UserAgent neo = MockAgentFactory.getEve(); + neo.unlockPrivateKey("evespass"); + GroupAgent group = GroupAgent.createGroupAgent(new Agent[] { smith, neo }); + group.unlockPrivateKey(neo); + // store agents + firstNode.storeAgent(group); + PastryNodeImpl secondNode = nodes.get(1); + GroupAgent fetched = (GroupAgent) secondNode.getAgent(group.getId()); + // check attributes of both GroupAgents + Assert.assertEquals(group.getId(), fetched.getId()); + Assert.assertEquals(group.getName(), fetched.getName()); + Assert.assertEquals(group.getPublicKey(), fetched.getPublicKey()); + Assert.assertEquals(group.getSize(), fetched.getSize()); + Assert.assertArrayEquals(group.getMemberList(), fetched.getMemberList()); + // update GroupAgent on first node + UserAgent morpheus = MockAgentFactory.getAbel(); + group.addMember(morpheus); + firstNode.storeAgent(group); + // fetch GroupAgent again on second node + fetched = (GroupAgent) secondNode.getAgent(group.getId()); + // check (again) attributes of both GroupAgents + Assert.assertEquals(group.getId(), fetched.getId()); + Assert.assertEquals(group.getName(), fetched.getName()); + Assert.assertEquals(group.getPublicKey(), fetched.getPublicKey()); + Assert.assertEquals(group.getSize(), fetched.getSize()); + Assert.assertArrayEquals(group.getMemberList(), fetched.getMemberList()); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(); + } + } + +} diff --git a/src/test/java/i5/las2peer/persistency/EnvelopeTest.java b/src/test/java/i5/las2peer/persistency/EnvelopeTest.java index b4386226f..7a1c8aacb 100644 --- a/src/test/java/i5/las2peer/persistency/EnvelopeTest.java +++ b/src/test/java/i5/las2peer/persistency/EnvelopeTest.java @@ -33,7 +33,7 @@ public class EnvelopeTest { private ArrayList nodes; private boolean asyncTestState; - private static class ExceptionHandler implements StorageExceptionHandler { + private class ExceptionHandler implements StorageExceptionHandler { @Override public void onException(Exception e) { e.printStackTrace(); @@ -41,7 +41,7 @@ public void onException(Exception e) { } } - private static final ExceptionHandler storageExceptionHandler = new ExceptionHandler(); + private final ExceptionHandler storageExceptionHandler = new ExceptionHandler(); @Rule public TestName name = new TestName(); @@ -184,11 +184,12 @@ public void onEnvelopeReceived(Envelope envelope) { }, null, storageExceptionHandler); // wait till the envelope was fetched System.out.println("Waiting ..."); - for (int n = 1; n <= 100; n++) { + long timeout = System.currentTimeMillis() + 120000000L; // two minutes timeout + while (System.currentTimeMillis() < timeout) { if (asyncTestState) { return; } - Thread.sleep(400); + Thread.sleep(200); } Assert.fail("Unexpected result"); } catch (Exception e) { @@ -307,7 +308,7 @@ public void testCollisionWithoutCollisionManager() { smith.unlockPrivateKey("adamspass"); Envelope envelope1 = node1.createUnencryptedEnvelope("test", "Hello World 1!"); Envelope envelope2 = node1.createUnencryptedEnvelope("test", "Hello World 2!"); - // upload envelope + // upload first envelope node1.storeEnvelopeAsync(envelope1, smith, new StorageStoreResultHandler() { @Override public void onResult(Serializable serializable, int successfulOperations) { @@ -320,13 +321,18 @@ public void onResult(Serializable serializable, int successfulOperations) { Assert.fail("Exception expected!"); } }, null, new StorageExceptionHandler() { + private boolean testComplete = false; + @Override public void onException(Exception e) { - if (e instanceof EnvelopeAlreadyExistsException) { - System.out.println("Expected exception '" + e.toString() + "' received."); - asyncTestState = true; - } else { - storageExceptionHandler.onException(e); + synchronized (this) { + if (e instanceof EnvelopeAlreadyExistsException) { + System.out.println("Expected exception '" + e.toString() + "' received."); + testComplete = true; + asyncTestState = true; + } else if (!testComplete) { // test yet incomplete + storageExceptionHandler.onException(e); + } } } }); diff --git a/src/test/java/i5/las2peer/persistency/TestCryptoMethods.java b/src/test/java/i5/las2peer/persistency/TestCryptoMethods.java index 5a2e61555..ac1d036a6 100755 --- a/src/test/java/i5/las2peer/persistency/TestCryptoMethods.java +++ b/src/test/java/i5/las2peer/persistency/TestCryptoMethods.java @@ -12,6 +12,7 @@ import java.security.NoSuchAlgorithmException; import java.security.Signature; import java.security.SignatureException; +import java.util.Base64; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -20,7 +21,6 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; -import org.apache.commons.codec.binary.Base64; import org.junit.Test; public class TestCryptoMethods { @@ -121,9 +121,9 @@ public void testBase64() throws UnsupportedEncodingException { String testMessage = "Dies ist eine längere Nachricht, die ich gerne in Base64 encodieren möchte."; byte[] bytes = testMessage.getBytes("UTF-8"); - String encoded = Base64.encodeBase64String(bytes); + String encoded = Base64.getEncoder().encodeToString(bytes); - byte[] decoded = Base64.decodeBase64(encoded); + byte[] decoded = Base64.getDecoder().decode(encoded); String decodedMessage = new String(decoded, "UTF-8");