Validate etc/gerrit.config file upon PUT request
Make sure that the content of an etc/gerrit.config file that is uploaded
by this plugin is syntactically correct by asserting that the
config can be loaded. No semantic validation is performed, however.
Change-Id: I400cca45ddb47e44aa304a304aabc00109736ab3
Signed-off-by: Adrian Görler <adrian.goerler@sap.com>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/serverconfig/ServerConfigServlet.java b/src/main/java/com/googlesource/gerrit/plugins/serverconfig/ServerConfigServlet.java
index 07f081d..da7c9c8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/serverconfig/ServerConfigServlet.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/serverconfig/ServerConfigServlet.java
@@ -14,6 +14,7 @@
package com.googlesource.gerrit.plugins.serverconfig;
+import com.google.common.base.Charsets;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.io.ByteStreams;
@@ -29,7 +30,13 @@
import com.google.inject.Singleton;
import org.eclipse.jgit.diff.RawText;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -37,6 +44,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.text.MessageFormat;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -45,6 +53,8 @@
@Singleton
public class ServerConfigServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
+ private static final Logger log = LoggerFactory
+ .getLogger(ServerConfigServlet.class);
private final File site_path;
private final File etc_dir;
@@ -94,20 +104,53 @@
}
}
- private void writeFileAndFireAuditEvent(HttpServletRequest req, HttpServletResponse res)
- throws IOException {
+ private void writeFileAndFireAuditEvent(HttpServletRequest req,
+ HttpServletResponse res) throws IOException {
File oldFile = configFile(req);
File dir = oldFile.getParentFile();
File newFile = File.createTempFile(oldFile.getName(), ".new", dir);
streamRequestToFile(req, newFile);
- String diff = diff(oldFile, newFile);
- audit("about to change config file", oldFile.getPath(), diff);
+ try {
+ FileBasedConfig config = new FileBasedConfig(newFile, FS.DETECTED);
+ config.load();
- newFile.renameTo(oldFile);
- audit("changed config file", oldFile.getPath(), diff);
+ String diff = diff(oldFile, newFile);
+ audit("about to change config file", oldFile.getPath(), diff);
- res.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ newFile.renameTo(oldFile);
+ audit("changed config file", oldFile.getPath(), diff);
+
+ res.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ } catch (ConfigInvalidException e) {
+ log.warn("Configuration file is invalid", e);
+
+ Throwable cause = e.getCause();
+ final String msg =
+ cause instanceof ConfigInvalidException ? cause.getMessage()
+ : e.getMessage();
+
+ newFile.delete();
+ respondInvalidConfig(req, res, msg);
+ }
+ }
+
+ private void respondInvalidConfig(HttpServletRequest req,
+ HttpServletResponse res, String messageTxt) throws IOException {
+ String message =
+ MessageFormat.format("Invalid config file {0}: {1}", req.getPathInfo(),
+ messageTxt);
+ res.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+ res.setContentType("application/octet-stream");
+ res.setContentLength(message.length());
+ byte[] bytes = message.getBytes(Charsets.UTF_8);
+ ByteArrayInputStream in = new ByteArrayInputStream(bytes);
+ OutputStream out = res.getOutputStream();
+ try {
+ ByteStreams.copy(in, out);
+ } finally {
+ in.close();
+ }
}
private static String diff(File oldFile, File newFile) throws IOException {