org.update4j.mapper.ConfigMapper Maven / Gradle / Ivy
/*
* Copyright 2018 Mordechai Meisels
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.update4j.mapper;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.update4j.OS;
import org.update4j.Property;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/*
* Everything that can be replaced by a property should be stored as strings.
* Resolve them in Configuration::read.
*
*/
public class ConfigMapper extends XmlMapper {
public String timestamp;
public String signature;
public String baseUri;
public String basePath;
public String updateHandler;
public String launcher;
public final List properties;
public final List files;
public ConfigMapper() {
properties = new ArrayList<>();
files = new ArrayList<>();
}
public ConfigMapper(Node node) {
this();
parse(node);
}
public ConfigMapper(ConfigMapper copy) {
this();
timestamp = copy.timestamp;
signature = copy.signature;
baseUri = copy.baseUri;
basePath = copy.basePath;
updateHandler = copy.updateHandler;
launcher = copy.launcher;
properties.addAll(copy.properties);
files.addAll(copy.files.stream().map(FileMapper::new).collect(Collectors.toList()));
}
@Override
public void parse(Node node) {
if (!"configuration".equals(node.getNodeName()))
return;
timestamp = getAttributeValue(node, "timestamp");
signature = getAttributeValue(node, "signature");
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node n = children.item(i);
if ("base".equals(n.getNodeName())) {
baseUri = getAttributeValue(n, "uri");
basePath = getAttributeValue(n, "path");
} else if ("provider".equals(n.getNodeName())) {
updateHandler = getAttributeValue(n, "updateHandler");
launcher = getAttributeValue(n, "launcher");
} else if ("properties".equals(n.getNodeName())) {
parseProperties(n.getChildNodes());
} else if ("files".equals(n.getNodeName())) {
parseFiles(n.getChildNodes());
}
}
}
private void parseProperties(NodeList list) {
for (int i = 0; i < list.getLength(); i++) {
Node n = list.item(i);
if ("property".equals(n.getNodeName())) {
String key = getAttributeValue(n, "key");
String value = getAttributeValue(n, "value");
String os = getAttributeValue(n, "os");
OS osEnum = null;
if (os != null)
osEnum = OS.fromShortName(os);
if (key != null && value != null) {
properties.add(new Property(key, value, osEnum));
}
}
}
}
private void parseFiles(NodeList list) {
for (int i = 0; i < list.getLength(); i++) {
Node n = list.item(i);
if ("file".equals(n.getNodeName())) {
files.add(new FileMapper(n));
}
}
}
@Override
public String toXml() {
StringBuilder builder = new StringBuilder();
builder.append("\n");
builder.append(children);
builder.append(" ");
} else {
builder.append("/>\n");
}
return builder.toString();
}
private String getChildrenXml() {
// no children
if (baseUri == null && basePath == null && updateHandler == null && launcher == null && properties.isEmpty()
&& files.isEmpty()) {
return "";
}
StringBuilder builder = new StringBuilder();
if (baseUri != null || basePath != null) {
builder.append(" \n");
}
if (updateHandler != null || launcher != null) {
builder.append(" \n");
}
if (!properties.isEmpty()) {
builder.append(" \n");
for (Property p : properties) {
builder.append(" \n");
}
builder.append(" \n");
}
if (!files.isEmpty()) {
builder.append(" \n");
for (FileMapper fm : files) {
builder.append(fm.toXml());
}
builder.append(" \n");
}
return builder.toString();
}
public String sign(PrivateKey key) {
try {
Signature sign = Signature.getInstance("SHA256with" + key.getAlgorithm());
sign.initSign(key);
sign.update(getChildrenXml().getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(sign.sign());
} catch (InvalidKeyException | SignatureException e) {
throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
public void verifySignature(PublicKey key) {
if (signature == null) {
throw new SecurityException("No signature in configuration root node.");
}
try {
Signature sign = Signature.getInstance("SHA256with" + key.getAlgorithm());
sign.initVerify(key);
sign.update(getChildrenXml().getBytes(StandardCharsets.UTF_8));
if (!sign.verify(Base64.getDecoder().decode(signature))) {
throw new SecurityException("Signature verification failed.");
}
} catch (InvalidKeyException | SignatureException | NoSuchAlgorithmException e) {
throw new SecurityException(e);
}
}
// @Override
// public Node toNode(Document doc) {
// Element e = doc.createElement("configuration");
//
// if (timestamp != null)
// e.setAttribute("timestamp", timestamp);
//
// if (baseUri != null && basePath != null) {
// Element base = doc.createElement("base");
//
// if (baseUri != null)
// base.setAttribute("uri", baseUri);
// if (basePath != null)
// base.setAttribute("path", basePath);
//
// e.appendChild(base);
// }
//
// if (updateHandler != null && launcher != null) {
// Element provider = doc.createElement("provider");
//
// if (updateHandler != null)
// provider.setAttribute("updateHandler", updateHandler);
// if (launcher != null)
// provider.setAttribute("launcher", launcher);
//
// e.appendChild(provider);
// }
//
// if (properties != null && properties.size() > 0) {
//
// Element props = doc.createElement("properties");
//
// for (Property p : properties) {
// Element prop = doc.createElement("property");
// prop.setAttribute("key", p.getKey());
// prop.setAttribute("value", p.getValue());
//
// if (p.getOs() != null) {
// prop.setAttribute("os", p.getOs()
// .getShortName());
// }
//
// props.appendChild(prop);
// }
//
// e.appendChild(props);
// }
//
// if (files != null && files.size() > 0) {
//
// Element f = doc.createElement("files");
//
// for (FileMapper fm : files) {
// f.appendChild(fm.toNode(doc));
// }
//
// e.appendChild(f);
// }
//
// return e;
// }
public static ConfigMapper read(Reader reader) throws IOException {
try {
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(reader));
NodeList list = doc.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node n = list.item(i);
if ("configuration".equals(n.getNodeName())) {
return new ConfigMapper(n);
}
}
throw new IllegalStateException("Root element must be 'configuration'.");
} catch (SAXException | ParserConfigurationException e) {
throw new IOException(e);
}
}
public void write(Writer writer) throws IOException {
write(writer, true);
}
public void write(Writer writer, boolean header) throws IOException {
if (header) {
writer.write("\n");
writer.write("\n");
writer.write("\n");
}
writer.write(toXml());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy