From 8f3a609e032e9ec37b791595bfef8d6579d5d9d2 Mon Sep 17 00:00:00 2001
From: Timm Fitschen <timm.fitschen@ds.mpg.de>
Date: Wed, 28 Nov 2018 14:29:05 +0100
Subject: [PATCH] EHN: Change server properties on-the-fly

... but only with permissions and in debug-mode
---
 caosdb-webui                                  |  2 +-
 src/main/java/caosdb/server/CaosDBServer.java | 14 +++++--
 .../server/ServerPropertiesSerializer.java    | 36 ++++++++++++++++
 .../server/accessControl/ACMPermissions.java  |  1 +
 .../AbstractCaosDBServerResource.java         | 24 +++++++----
 .../resource/ServerPropertiesResource.java    | 42 +++++++++++++++++++
 6 files changed, 107 insertions(+), 12 deletions(-)
 create mode 100644 src/main/java/caosdb/server/ServerPropertiesSerializer.java
 create mode 100644 src/main/java/caosdb/server/resource/ServerPropertiesResource.java

diff --git a/caosdb-webui b/caosdb-webui
index 7df3c8cd..2a6d74d7 160000
--- a/caosdb-webui
+++ b/caosdb-webui
@@ -1 +1 @@
-Subproject commit 7df3c8cdf945e5b98df8b04a084fabbdaf058946
+Subproject commit 2a6d74d7cb6bed89b2e7a0e5801ff4c040a3ebf8
diff --git a/src/main/java/caosdb/server/CaosDBServer.java b/src/main/java/caosdb/server/CaosDBServer.java
index 8c67d514..d52b6e15 100644
--- a/src/main/java/caosdb/server/CaosDBServer.java
+++ b/src/main/java/caosdb/server/CaosDBServer.java
@@ -48,6 +48,7 @@ import caosdb.server.resource.PermissionRulesResource;
 import caosdb.server.resource.RolesResource;
 import caosdb.server.resource.ScriptingResource;
 import caosdb.server.resource.ServerLogsResource;
+import caosdb.server.resource.ServerPropertiesResource;
 import caosdb.server.resource.TestCaseFileSystemResource;
 import caosdb.server.resource.TestCaseResource;
 import caosdb.server.resource.ThumbnailsResource;
@@ -111,10 +112,7 @@ public class CaosDBServer extends Application {
   private static boolean INSECURE = false;
 
   public static String getServerProperty(final String key) {
-    if (SERVER_PROPERTIES == null) {
-      SERVER_PROPERTIES = ServerProperties.initServerProperties();
-    }
-    return SERVER_PROPERTIES.getProperty(key);
+    return getServerProperties().getProperty(key);
   }
 
   /**
@@ -581,6 +579,7 @@ public class CaosDBServer extends Application {
         .setMatchingMode(Template.MODE_STARTS_WITH);
     protectedRouter.attach("/login?username={username}", AuthenticationResource.class);
     protectedRouter.attach("/logout", LogoutResource.class);
+    protectedRouter.attach("/_server_properties", ServerPropertiesResource.class);
     protectedRouter.attachDefault(DefaultResource.class);
 
     /*
@@ -711,6 +710,13 @@ public class CaosDBServer extends Application {
     }
     SERVER_PROPERTIES.setProperty(key, value);
   }
+
+  public static Properties getServerProperties() {
+    if (SERVER_PROPERTIES == null) {
+      SERVER_PROPERTIES = ServerProperties.initServerProperties();
+    }
+    return SERVER_PROPERTIES;
+  }
 }
 
 class CaosDBComponent extends Component {
diff --git a/src/main/java/caosdb/server/ServerPropertiesSerializer.java b/src/main/java/caosdb/server/ServerPropertiesSerializer.java
new file mode 100644
index 00000000..10189f99
--- /dev/null
+++ b/src/main/java/caosdb/server/ServerPropertiesSerializer.java
@@ -0,0 +1,36 @@
+package caosdb.server;
+
+import caosdb.server.utils.Serializer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import org.jdom2.Element;
+
+public class ServerPropertiesSerializer implements Serializer<Properties, Element> {
+
+  private boolean sort;
+
+  public ServerPropertiesSerializer(boolean sort) {
+    this.sort = sort;
+  }
+
+  public ServerPropertiesSerializer() {
+    this(true);
+  }
+
+  @Override
+  public Element serialize(Properties object) {
+    Element root = new Element("Properties");
+    List<String> names = new ArrayList<>(object.stringPropertyNames());
+    if (sort) {
+      Collections.sort(names);
+    }
+    for (String prop : names) {
+      Element propElem = new Element(prop);
+      propElem.addContent(object.getProperty(prop));
+      root.addContent(propElem);
+    }
+    return root;
+  }
+}
diff --git a/src/main/java/caosdb/server/accessControl/ACMPermissions.java b/src/main/java/caosdb/server/accessControl/ACMPermissions.java
index 40b063f1..29988106 100644
--- a/src/main/java/caosdb/server/accessControl/ACMPermissions.java
+++ b/src/main/java/caosdb/server/accessControl/ACMPermissions.java
@@ -24,6 +24,7 @@ package caosdb.server.accessControl;
 
 public class ACMPermissions {
 
+  public static final String PERMISSION_ACCESS_SERVER_PROPERTIES = "ACCESS_SERVER_PROPERTIES";
   public static final String PERMISSION_RETRIEVE_SERVERLOGS = "SERVERLOGS:RETRIEVE";
 
   public static final String PERMISSION_RETRIEVE_USER_ROLES(
diff --git a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
index 27d81e72..3057ce9f 100644
--- a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
+++ b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
@@ -267,19 +267,29 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
     return new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript());
   }
 
-  protected JdomRepresentation error(final Message m, final Status status) {
+  protected Representation error(final Message m, final Status status) {
     final Document doc = new Document();
     final Element root = generateRootElement();
     root.addContent(m.toElement().setName("Error"));
     doc.setRootElement(root);
+    return error(
+        new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript()),
+        status);
+  }
+
+  protected Representation error(Representation entity, Status status) {
     getResponse().setStatus(status);
-    return new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript());
+    return entity;
   }
 
-  protected JdomRepresentation error(final Message m) {
+  protected Representation error(final Message m) {
     return error(m, Status.SERVER_ERROR_INTERNAL);
   }
 
+  protected Representation error(final Status status) {
+    return error((Representation) null, status);
+  }
+
   protected JdomRepresentation warning(final Message m) {
     final Document doc = new Document();
     final Element root = generateRootElement();
@@ -288,19 +298,19 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
     return new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript());
   }
 
-  protected JdomRepresentation noWellFormedNess() {
+  protected Representation noWellFormedNess() {
     return error(ServerMessages.REQUEST_BODY_NOT_WELLFORMED);
   }
 
-  protected JdomRepresentation emptyEntity() {
+  protected Representation emptyEntity() {
     return error(ServerMessages.REQUEST_BODY_EMPTY);
   }
 
-  protected JdomRepresentation connectionFailed() {
+  protected Representation connectionFailed() {
     return error(ServerMessages.CANNOT_CONNECT_TO_DATABASE);
   }
 
-  public JdomRepresentation handleThrowable(final Throwable t) {
+  public Representation handleThrowable(final Throwable t) {
     try {
       getRequest().getAttributes().put("THROWN", t);
       throw t;
diff --git a/src/main/java/caosdb/server/resource/ServerPropertiesResource.java b/src/main/java/caosdb/server/resource/ServerPropertiesResource.java
new file mode 100644
index 00000000..9b878e33
--- /dev/null
+++ b/src/main/java/caosdb/server/resource/ServerPropertiesResource.java
@@ -0,0 +1,42 @@
+package caosdb.server.resource;
+
+import caosdb.server.CaosDBServer;
+import caosdb.server.ServerPropertiesSerializer;
+import caosdb.server.accessControl.ACMPermissions;
+import org.restlet.data.Form;
+import org.restlet.data.Parameter;
+import org.restlet.data.Status;
+import org.restlet.representation.Representation;
+
+public class ServerPropertiesResource extends AbstractCaosDBServerResource {
+
+  @Override
+  protected void doInit() {
+    super.doInit();
+    setXSLScript("xsl/server_properties.xsl");
+  }
+
+  @Override
+  protected Representation httpGetInChildClass() {
+    if (CaosDBServer.isDebugMode()) {
+      getUser().checkPermission(ACMPermissions.PERMISSION_ACCESS_SERVER_PROPERTIES);
+      return super.ok(
+          new ServerPropertiesSerializer().serialize(CaosDBServer.getServerProperties()));
+    }
+    return error(Status.CLIENT_ERROR_NOT_FOUND);
+  }
+
+  @Override
+  protected Representation httpPostInChildClass(Representation entity) {
+    if (CaosDBServer.isDebugMode()) {
+      getUser().checkPermission(ACMPermissions.PERMISSION_ACCESS_SERVER_PROPERTIES);
+      Form form = new Form(entity);
+      for (Parameter param : form) {
+        CaosDBServer.setProperty(param.getName(), param.getValue());
+      }
+      return super.ok(
+          new ServerPropertiesSerializer().serialize(CaosDBServer.getServerProperties()));
+    }
+    return error(Status.CLIENT_ERROR_NOT_FOUND);
+  }
+}
-- 
GitLab