فهرست منبع

Improve logging.

- Add sanitised Config and test
- Fix issue with file permissions
Winston Li 10 سال پیش
والد
کامیت
a4df78c73d

+ 8 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/Main.java

@@ -1,6 +1,7 @@
 package uk.ac.ic.wlgitbridge;
 
 import uk.ac.ic.wlgitbridge.application.GitBridgeApp;
+import uk.ac.ic.wlgitbridge.util.Log;
 
 /**
  * Created by Winston on 01/11/14.
@@ -8,7 +9,13 @@ import uk.ac.ic.wlgitbridge.application.GitBridgeApp;
 public class Main {
 
     public static void main(String[] args) {
-        new GitBridgeApp(args).run();
+        try {
+            new GitBridgeApp(args).run();
+        } catch (Throwable t) {
+            /* So that we get a timestamp */
+            Log.error("Fatal exception thrown to top level, exiting: ", t);
+            System.exit(1);
+        }
     }
 
 }

+ 1 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/GitBridgeApp.java

@@ -36,7 +36,7 @@ public class GitBridgeApp implements Runnable {
         try {
             parseArguments(args);
             loadConfigFile();
-            Log.info("Config file loaded");
+            Log.info("Config loaded: {}", config.getSanitisedString());
         } catch (ArgsException e) {
             printUsage();
             System.exit(EXIT_CODE_FAILED);

+ 46 - 4
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/config/Config.java

@@ -3,9 +3,9 @@ package uk.ac.ic.wlgitbridge.application.config;
 import com.google.gson.Gson;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
-import com.google.gson.JsonParseException;
 import uk.ac.ic.wlgitbridge.application.exception.ConfigFileException;
 import uk.ac.ic.wlgitbridge.snapshot.base.JSONSource;
+import uk.ac.ic.wlgitbridge.util.Instance;
 
 import java.io.FileReader;
 import java.io.IOException;
@@ -16,6 +16,19 @@ import java.io.Reader;
  */
 public class Config implements JSONSource {
 
+    static Config asSanitised(Config config) {
+        return new Config(
+                config.port,
+                config.rootGitDirectory,
+                config.username,
+                "<password>",
+                config.apiBaseURL,
+                config.postbackURL,
+                config.serviceName,
+                Oauth2.asSanitised(config.oauth2)
+        );
+    }
+
     private int port;
     private String rootGitDirectory;
     private String username;
@@ -25,7 +38,8 @@ public class Config implements JSONSource {
     private String serviceName;
     private Oauth2 oauth2;
 
-    public Config(String configFilePath) throws ConfigFileException, IOException {
+    public Config(String configFilePath) throws ConfigFileException,
+                                                IOException {
         this(new FileReader(configFilePath));
     }
 
@@ -33,14 +47,38 @@ public class Config implements JSONSource {
         fromJSON(new Gson().fromJson(reader, JsonElement.class));
     }
 
+    public Config(int port,
+                  String rootGitDirectory,
+                  String username,
+                  String password,
+                  String apiBaseURL,
+                  String postbackURL,
+                  String serviceName,
+                  Oauth2 oauth2) {
+        this.port = port;
+        this.rootGitDirectory = rootGitDirectory;
+        this.username = username;
+        this.password = password;
+        this.apiBaseURL = apiBaseURL;
+        this.postbackURL = postbackURL;
+        this.serviceName = serviceName;
+        this.oauth2 = oauth2;
+    }
+
     @Override
     public void fromJSON(JsonElement json) {
         JsonObject configObject = json.getAsJsonObject();
         port = getElement(configObject, "port").getAsInt();
-        rootGitDirectory = getElement(configObject, "rootGitDirectory").getAsString();
+        rootGitDirectory = getElement(
+                configObject,
+                "rootGitDirectory"
+        ).getAsString();
         username = getOptionalString(configObject, "username");
         password = getOptionalString(configObject, "password");
-        String apiBaseURL = getElement(configObject, "apiBaseUrl").getAsString();
+        String apiBaseURL = getElement(
+                configObject,
+                "apiBaseUrl"
+        ).getAsString();
         if (!apiBaseURL.endsWith("/")) {
             apiBaseURL += "/";
         }
@@ -53,6 +91,10 @@ public class Config implements JSONSource {
         oauth2 = new Gson().fromJson(configObject.get("oauth2"), Oauth2.class);
     }
 
+    public String getSanitisedString() {
+        return Instance.prettyGson.toJson(Config.asSanitised(this));
+    }
+
     public int getPort() {
         return port;
     }

+ 8 - 0
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/config/Oauth2.java

@@ -27,4 +27,12 @@ public class Oauth2 {
         return oauth2Server;
     }
 
+    public static Oauth2 asSanitised(Oauth2 oauth2) {
+        return new Oauth2(
+                "<oauth2ClientID>",
+                "<oauth2ClientSecret>",
+                oauth2.oauth2Server
+        );
+    }
+
 }

+ 7 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/data/model/db/sql/SQLiteWLDatabase.java

@@ -9,6 +9,7 @@ import uk.ac.ic.wlgitbridge.data.model.db.sql.update.create.CreateURLIndexStoreS
 import uk.ac.ic.wlgitbridge.data.model.db.sql.update.delete.DeleteFilesForProjectSQLUpdate;
 import uk.ac.ic.wlgitbridge.data.model.db.sql.update.insert.AddURLIndexSQLUpdate;
 import uk.ac.ic.wlgitbridge.data.model.db.sql.update.insert.SetProjectSQLUpdate;
+import uk.ac.ic.wlgitbridge.util.Log;
 
 import java.io.File;
 import java.sql.*;
@@ -23,7 +24,12 @@ public class SQLiteWLDatabase {
 
     public SQLiteWLDatabase(File rootGitDirectory) throws SQLException, ClassNotFoundException {
         File databaseFile = new File(rootGitDirectory, "/.wlgb/wlgb.db");
-        databaseFile.getParentFile().mkdirs();
+        File dotWlgbDir = databaseFile.getParentFile();
+        if (!dotWlgbDir.exists()) {
+            if (!dotWlgbDir.mkdirs()) {
+                Log.error("{} directory didn't exist, and unable to create. Check your permissions", dotWlgbDir.getAbsolutePath());
+            }
+        }
         Class.forName("org.sqlite.JDBC");
         connection = DriverManager.getConnection("jdbc:sqlite:" + databaseFile.getAbsolutePath());
         createTables();

+ 30 - 14
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/Request.java

@@ -1,16 +1,13 @@
 package uk.ac.ic.wlgitbridge.snapshot.base;
 
 import com.google.api.client.http.*;
-import com.google.gson.Gson;
 import com.google.gson.JsonElement;
-import com.ning.http.client.AsyncCompletionHandler;
 import com.ning.http.client.AsyncHttpClient;
-import com.ning.http.client.Response;
 import uk.ac.ic.wlgitbridge.snapshot.exception.FailedConnectionException;
 import uk.ac.ic.wlgitbridge.util.Instance;
+import uk.ac.ic.wlgitbridge.util.Log;
 import uk.ac.ic.wlgitbridge.util.Util;
 
-import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
@@ -47,22 +44,32 @@ public abstract class Request<T extends Result> {
     public T getResult() throws FailedConnectionException, ForbiddenException {
         try {
             HttpResponse response = future.get();
-            Util.sout(response.getStatusCode() + " " + response.getStatusMessage() + " (" + response.getHeaders().getContentLength() + "B) -> " + url);
-            JsonElement json = new Gson().fromJson(response.parseAsString(), JsonElement.class);
+            Log.info(
+                    "{} {} ({}B) -> " + url,
+                    response.getStatusCode(),
+                    response.getStatusMessage(),
+                    response.getHeaders().getContentLength()
+            );
+            JsonElement json = Instance.gson.fromJson(
+                    response.parseAsString(),
+                    JsonElement.class
+            );
             return parseResponse(json);
         } catch (InterruptedException e) {
             throw new FailedConnectionException();
         } catch (ExecutionException e) {
             Throwable cause = e.getCause();
-            int statusCode = ((HttpResponseException) cause).getStatusCode();
-            if (cause instanceof HttpResponseException && (statusCode == HttpServletResponse.SC_UNAUTHORIZED || statusCode == HttpServletResponse.SC_FORBIDDEN)) {
+            if (cause instanceof HttpResponseException &&
+                    (((HttpResponseException) cause).getStatusCode() ==
+                            HttpServletResponse.SC_UNAUTHORIZED ||
+                    ((HttpResponseException) cause).getStatusCode() ==
+                            HttpServletResponse.SC_FORBIDDEN)) {
                 throw new ForbiddenException();
             } else {
-                throw new FailedConnectionException();
+                throw new FailedConnectionException(cause);
             }
         } catch (IOException e) {
-            Util.serr("Failed to parse JSON");
-            e.printStackTrace();
+            Log.error("Failed to parse JSON.", e);
             throw new FailedConnectionException();
         }
     }
@@ -73,7 +80,8 @@ public abstract class Request<T extends Result> {
 
     }
 
-    protected abstract T parseResponse(JsonElement json) throws FailedConnectionException;
+    protected abstract
+    T parseResponse(JsonElement json) throws FailedConnectionException;
 
     protected String getPostBody() {
         return null;
@@ -82,7 +90,9 @@ public abstract class Request<T extends Result> {
     private void performGetRequest() {
         Util.sout("GET -> " + url);
         try {
-            HttpRequest request = Instance.httpRequestFactory.buildGetRequest(new GenericUrl(url));
+            HttpRequest request = Instance.httpRequestFactory.buildGetRequest(
+                    new GenericUrl(url)
+            );
             request(request);
         } catch (IOException e) {
             e.printStackTrace();
@@ -93,7 +103,13 @@ public abstract class Request<T extends Result> {
     private void performPostRequest() {
         Util.sout("POST -> " + url);
         try {
-            HttpRequest request = Instance.httpRequestFactory.buildPostRequest(new GenericUrl(url), new ByteArrayContent("application/json", getPostBody().getBytes()));
+            HttpRequest request = Instance.httpRequestFactory.buildPostRequest(
+                    new GenericUrl(url),
+                    new ByteArrayContent(
+                            "application/json",
+                            getPostBody().getBytes()
+                    )
+            );
             request(request);
         } catch (IOException e) {
             e.printStackTrace();

+ 6 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/exception/FailedConnectionException.java

@@ -9,7 +9,12 @@ import uk.ac.ic.wlgitbridge.util.Util;
 public class FailedConnectionException extends ServiceMayNotContinueException {
 
     public FailedConnectionException() {
-        super(Util.getServiceName() + " server not available. Please try again later.");
+        super(Util.getServiceName() +
+                " server not available. Please try again later.");
+    }
+
+    public FailedConnectionException(Throwable cause) {
+        super(cause);
     }
 
 }

+ 13 - 2
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Instance.java

@@ -5,14 +5,25 @@ import com.google.api.client.http.HttpTransport;
 import com.google.api.client.http.javanet.NetHttpTransport;
 import com.google.api.client.json.JsonFactory;
 import com.google.api.client.json.gson.GsonFactory;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
 
 /**
  * Created by winston on 25/10/15.
  */
 public class Instance {
 
-    public static final HttpTransport httpTransport = new NetHttpTransport();
-    public static final HttpRequestFactory httpRequestFactory = httpTransport.createRequestFactory();
+    public static final HttpTransport httpTransport =
+            new NetHttpTransport();
+
+    public static final HttpRequestFactory httpRequestFactory =
+            httpTransport.createRequestFactory();
+
     public static final JsonFactory jsonFactory = new GsonFactory();
 
+    public static final Gson prettyGson =
+            new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
+
+    public static final Gson gson = new Gson();
+
 }

+ 4 - 0
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Log.java

@@ -77,6 +77,10 @@ public class Log {
         logger.error(msg);
     }
 
+    public static void error(String msg, Object... args) {
+        logger.error(msg, args);
+    }
+
     public static void error(String msg, Throwable t) {
         logger.error(msg, t);
     }

+ 2 - 7
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/util/Util.java

@@ -90,7 +90,7 @@ public class Util {
     }
 
     private static void println(PrintStream ps, String ln) {
-        ps.println(getStringBuilder().append(ln));
+        Log.info(ln);
     }
 
     public static void sout(String ln) {
@@ -108,10 +108,6 @@ public class Util {
         return sb;
     }
 
-    public static void sout() {
-        sout("");
-    }
-
     public static void serr() {
         serr("");
     }
@@ -121,8 +117,7 @@ public class Util {
     }
 
     public static void printStackTrace(Throwable t) {
-        serr();
-        t.printStackTrace();
+        Log.warn("Exception", t);
     }
 
     public static void deleteDirectory(File directory) {

+ 38 - 0
services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/application/config/ConfigTest.java

@@ -66,4 +66,42 @@ public class ConfigTest {
         config.getOauth2();
     }
 
+    @Test
+    public void asSanitised() throws Exception {
+        Reader reader = new StringReader("{\n" +
+                "    \"port\": 80,\n" +
+                "    \"rootGitDirectory\": \"/var/wlgb/git\",\n" +
+                "    \"apiBaseUrl\": \"http://127.0.0.1:60000/api/v0\",\n" +
+                "    \"username\": \"username\",\n" +
+                "    \"password\": \"my super secret password\",\n" +
+                "    \"postbackBaseUrl\": \"http://127.0.0.1\",\n" +
+                "    \"serviceName\": \"Overleaf\",\n" +
+                "    \"oauth2\": {\n" +
+                "        \"oauth2ClientID\": \"my oauth2 client id\",\n" +
+                "        \"oauth2ClientSecret\": \"my oauth2 client secret\",\n" +
+                "        \"oauth2Server\": \"https://www.overleaf.com\"\n" +
+                "    }\n" +
+                "}\n");
+        Config config = new Config(reader);
+        String expected = "{\n" +
+                "  \"port\": 80,\n" +
+                "  \"rootGitDirectory\": \"/var/wlgb/git\",\n" +
+                "  \"username\": \"username\",\n" +
+                "  \"password\": \"<password>\",\n" +
+                "  \"apiBaseURL\": \"http://127.0.0.1:60000/api/v0/\",\n" +
+                "  \"postbackURL\": \"http://127.0.0.1/\",\n" +
+                "  \"serviceName\": \"Overleaf\",\n" +
+                "  \"oauth2\": {\n" +
+                "    \"oauth2ClientID\": \"<oauth2ClientID>\",\n" +
+                "    \"oauth2ClientSecret\": \"<oauth2ClientSecret>\",\n" +
+                "    \"oauth2Server\": \"https://www.overleaf.com\"\n" +
+                "  }\n" +
+                "}";
+        assertEquals(
+                "sanitised config did not hide sensitive fields",
+                expected,
+                config.getSanitisedString()
+        );
+    }
+
 }