Просмотр исходного кода

(fix #8) Add better error message for 403 protected projects with integration test and support.

Winston Li 11 лет назад
Родитель
Сommit
ac50c26aef

+ 2 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/WriteLatexDataSource.java

@@ -1,5 +1,6 @@
 package uk.ac.ic.wlgitbridge.bridge;
 
+import org.eclipse.jgit.transport.ServiceMayNotContinueException;
 import uk.ac.ic.wlgitbridge.writelatex.api.request.exception.FailedConnectionException;
 import uk.ac.ic.wlgitbridge.writelatex.api.request.getdoc.exception.InvalidProjectException;
 import uk.ac.ic.wlgitbridge.writelatex.api.request.push.exception.InvalidPostbackKeyException;
@@ -19,7 +20,7 @@ public interface WriteLatexDataSource {
     void unlockForProject(String projectName);
 
     /* Called by request thread. */
-    public boolean repositoryExists(String projectName) throws FailedConnectionException;
+    public boolean repositoryExists(String projectName) throws ServiceMayNotContinueException;
     public List<WritableRepositoryContents> getWritableRepositories(String projectName) throws FailedConnectionException, InvalidProjectException;
     public void putDirectoryContentsToProjectWithName(String projectName, RawDirectoryContents directoryContents, String hostname) throws SnapshotPostException, IOException, FailedConnectionException;
 

+ 0 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLRepositoryResolver.java

@@ -41,7 +41,6 @@ public class WLRepositoryResolver implements RepositoryResolver<HttpServletReque
             cannot occur
             */
         } catch (ServiceMayNotContinueException e) { /* Such as FailedConnectionException */
-            Util.printStackTrace(e);
             throw e;
         } catch (RuntimeException e) {
             Util.printStackTrace(e);

+ 1 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/test/state/SnapshotAPIState.java

@@ -41,7 +41,7 @@ public class SnapshotAPIState {
     public SnapshotAPIState() {
 
         getDoc = new HashMap<String, SnapshotGetDocResult>();
-        getDoc.put("1826rqgsdb", new SnapshotGetDocResult(243, "2014-11-30T18:40:58Z", "jdleesmiller+1@gmail.com", "John+1"));
+        getDoc.put("1826rqgsdb", new SnapshotGetDocResult(null, 243, "2014-11-30T18:40:58Z", "jdleesmiller+1@gmail.com", "John+1"));
 
         getSavedVers = new HashMap<String, SnapshotGetSavedVersResult>();
         List<SnapshotInfo> savedVers = new LinkedList<SnapshotInfo>();

+ 2 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/test/state/SnapshotAPIStateBuilder.java

@@ -59,7 +59,8 @@ public class SnapshotAPIStateBuilder {
 
     private void addGetDocForProject(String projectName, JsonObject jsonGetDoc) {
         getDoc.put(projectName,
-                   new SnapshotGetDocResult(jsonGetDoc.get("versionID").getAsInt(),
+                   new SnapshotGetDocResult(jsonGetDoc.get("error"),
+                                            jsonGetDoc.get("versionID").getAsInt(),
                                             jsonGetDoc.get("createdAt").getAsString(),
                                             jsonGetDoc.get("email").getAsString(),
                                             jsonGetDoc.get("name").getAsString()));

+ 7 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/SnapshotFetcher.java

@@ -10,6 +10,7 @@ import uk.ac.ic.wlgitbridge.writelatex.api.request.getforversion.SnapshotGetForV
 import uk.ac.ic.wlgitbridge.writelatex.api.request.getforversion.SnapshotGetForVersionResult;
 import uk.ac.ic.wlgitbridge.writelatex.api.request.getsavedvers.SnapshotGetSavedVersRequest;
 import uk.ac.ic.wlgitbridge.writelatex.api.request.getsavedvers.SnapshotInfo;
+import uk.ac.ic.wlgitbridge.writelatex.api.request.push.exception.SnapshotPostException;
 import uk.ac.ic.wlgitbridge.writelatex.model.Snapshot;
 import uk.ac.ic.wlgitbridge.writelatex.model.db.PersistentStoreAPI;
 import uk.ac.ic.wlgitbridge.writelatex.model.db.PersistentStoreSource;
@@ -93,7 +94,12 @@ public class SnapshotFetcher implements PersistentStoreSource {
 
     private int putLatestDoc(SnapshotGetDocRequest getDoc, Set<Integer> fetchedIDs, Map<Integer, SnapshotInfo> fetchedSnapshotInfos) throws FailedConnectionException, InvalidProjectException {
         SnapshotGetDocResult result = getDoc.getResult();
-        int latestVersionID = result.getVersionID();
+        int latestVersionID = 0;
+        try {
+            latestVersionID = result.getVersionID();
+        } catch (SnapshotPostException e) {
+            throw new RuntimeException(e);
+        }
         putFetchedResult(new SnapshotInfo(latestVersionID, result.getCreatedAt(), result.getName(), result.getEmail()), fetchedIDs, fetchedSnapshotInfos);
         return latestVersionID;
     }

+ 4 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/WriteLatexAPI.java

@@ -1,5 +1,6 @@
 package uk.ac.ic.wlgitbridge.writelatex;
 
+import org.eclipse.jgit.transport.ServiceMayNotContinueException;
 import uk.ac.ic.wlgitbridge.bridge.CandidateSnapshot;
 import uk.ac.ic.wlgitbridge.bridge.RawDirectoryContents;
 import uk.ac.ic.wlgitbridge.bridge.WritableRepositoryContents;
@@ -43,7 +44,7 @@ public class WriteLatexAPI implements WriteLatexDataSource {
     }
 
     @Override
-    public boolean repositoryExists(String projectName) throws FailedConnectionException {
+    public boolean repositoryExists(String projectName) throws ServiceMayNotContinueException {
         lockForProject(projectName);
         SnapshotGetDocRequest snapshotGetDocRequest = new SnapshotGetDocRequest(projectName);
         snapshotGetDocRequest.request();
@@ -53,6 +54,8 @@ public class WriteLatexAPI implements WriteLatexDataSource {
             return false;
         } catch (FailedConnectionException e) {
             throw e;
+        } catch (SnapshotPostException e) {
+            throw new ServiceMayNotContinueException(e.getMessage());
         } finally {
             unlockForProject(projectName);
         }

+ 41 - 13
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/api/request/getdoc/SnapshotGetDocResult.java

@@ -6,24 +6,32 @@ import uk.ac.ic.wlgitbridge.writelatex.api.request.base.Request;
 import uk.ac.ic.wlgitbridge.writelatex.api.request.base.Result;
 import uk.ac.ic.wlgitbridge.writelatex.api.request.exception.FailedConnectionException;
 import uk.ac.ic.wlgitbridge.writelatex.api.request.getdoc.exception.InvalidProjectException;
+import uk.ac.ic.wlgitbridge.writelatex.api.request.getdoc.exception.ProtectedProjectException;
+import uk.ac.ic.wlgitbridge.writelatex.api.request.push.exception.SnapshotPostException;
 
 /**
  * Created by Winston on 06/11/14.
  */
 public class SnapshotGetDocResult extends Result {
 
+    private int error;
     private int versionID;
     private String createdAt;
     private String name;
     private String email;
 
-    private InvalidProjectException invalidProjectException;
+    private SnapshotPostException exception;
 
     public SnapshotGetDocResult(Request request, JsonElement json) throws FailedConnectionException {
         super(request, json);
     }
 
-    public SnapshotGetDocResult(int versionID, String createdAt, String email, String name) {
+    public SnapshotGetDocResult(JsonElement error, int versionID, String createdAt, String email, String name) {
+        if (error == null) {
+            this.error = -1;
+        } else {
+            this.error = error.getAsInt();
+        }
         this.versionID = versionID;
         this.createdAt = createdAt;
         this.name = name;
@@ -33,20 +41,40 @@ public class SnapshotGetDocResult extends Result {
     @Override
     public JsonElement toJson() {
         JsonObject jsonThis = new JsonObject();
-        jsonThis.addProperty("latestVerId", versionID);
-        jsonThis.addProperty("latestVerAt", createdAt);
-        JsonObject latestVerBy = new JsonObject();
-        latestVerBy.addProperty("email", email);
-        latestVerBy.addProperty("name", name);
-        jsonThis.add("latestVerBy", latestVerBy);
+        if (error == -1) {
+            jsonThis.addProperty("latestVerId", versionID);
+            jsonThis.addProperty("latestVerAt", createdAt);
+            JsonObject latestVerBy = new JsonObject();
+            latestVerBy.addProperty("email", email);
+            latestVerBy.addProperty("name", name);
+            jsonThis.add("latestVerBy", latestVerBy);
+        } else {
+            jsonThis.addProperty("status", error);
+            String message;
+            if (error == 403) {
+                message = "Forbidden";
+            } else {
+                message = "Not Found";
+            }
+            jsonThis.addProperty("message", message);
+        }
         return jsonThis;
     }
 
     @Override
     public void fromJSON(JsonElement json) {
         JsonObject jsonObject = json.getAsJsonObject();
-        if (jsonObject.has("status") && jsonObject.get("status").getAsInt() == 404) {
-            invalidProjectException = new InvalidProjectException();
+        if (jsonObject.has("status")) {
+            switch (jsonObject.get("status").getAsInt()) {
+            case 403:
+                exception = new ProtectedProjectException();
+                break;
+            case 404:
+                exception = new InvalidProjectException();
+                break;
+            default:
+                throw new IllegalArgumentException("unknown get doc error code");
+            }
         } else {
             versionID = jsonObject.get("latestVerId").getAsInt();
             createdAt = jsonObject.get("latestVerAt").getAsString();
@@ -62,9 +90,9 @@ public class SnapshotGetDocResult extends Result {
         }
     }
 
-    public int getVersionID() throws InvalidProjectException {
-        if (invalidProjectException != null) {
-            throw invalidProjectException;
+    public int getVersionID() throws SnapshotPostException {
+        if (exception != null) {
+            throw exception;
         }
         return versionID;
     }

+ 29 - 0
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/api/request/getdoc/exception/ProtectedProjectException.java

@@ -0,0 +1,29 @@
+package uk.ac.ic.wlgitbridge.writelatex.api.request.getdoc.exception;
+
+import com.google.gson.JsonElement;
+import uk.ac.ic.wlgitbridge.writelatex.api.request.push.exception.SnapshotPostException;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Created by Winston on 20/02/15.
+ */
+public class ProtectedProjectException extends SnapshotPostException {
+
+    @Override
+    public String getMessage() {
+        return "Your project is protected, and can't be cloned (yet).";
+    }
+
+    @Override
+    public List<String> getDescriptionLines() {
+        return Arrays.asList("You can't currently clone a protected project.");
+    }
+
+    @Override
+    public void fromJSON(JsonElement json) {
+
+    }
+
+}

+ 32 - 0
services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest.java

@@ -2,6 +2,7 @@ package uk.ac.ic.wlgitbridge;
 
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.TransportException;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -17,7 +18,9 @@ import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.Map;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 /**
  * Created by Winston on 11/01/15.
@@ -40,6 +43,9 @@ public class WLGitBridgeIntegrationTest {
                     put("base", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullADeletedTexFile/base/state.json")).build());
                     put("withDeletedTexFile", new SnapshotAPIStateBuilder(getResourceAsStream("/canPullADeletedTexFile/withDeletedTexFile/state.json")).build());
                 }});
+                put("cannotCloneAProtectedProject", new HashMap<String, SnapshotAPIState>() {{
+                    put("state", new SnapshotAPIStateBuilder(getResourceAsStream("/cannotCloneAProtectedProject/state/state.json")).build());
+                }});
             }};
 
     @Rule
@@ -138,6 +144,32 @@ public class WLGitBridgeIntegrationTest {
         assertTrue(FileUtil.gitDirectoriesAreEqual(getResource("/canPullADeletedTexFile/withDeletedTexFile/testproj"), git.toPath()));
     }
 
+    @Test
+    public void cannotCloneAProtectedProject() throws IOException, GitAPIException {
+        MockSnapshotServer server = new MockSnapshotServer(3861, getResource("/cannotCloneAProtectedProject").toFile());
+        server.start();
+        server.setState(states.get("cannotCloneAProtectedProject").get("state"));
+        WLGitBridgeApplication wlgb = new WLGitBridgeApplication(new String[] {
+                makeConfigFile(33861, 3861)
+        });
+        wlgb.run();
+        folder.create();
+        File git = folder.newFolder();
+        try {
+            Git.cloneRepository()
+                    .setURI("http://127.0.0.1:33861/protected.git")
+                    .setDirectory(git)
+                    .call()
+                    .close();
+        } catch (TransportException e) {
+            assertEquals("http://127.0.0.1:33861/protected.git: Your project is protected, and can't be cloned (yet).", e.getMessage());
+            return;
+        } finally {
+            wlgb.stop();
+        }
+        fail();
+    }
+
     private String makeConfigFile(int port, int apiPort) throws IOException {
         File wlgb = folder.newFolder();
         File config = folder.newFile();

+ 173 - 0
services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/cannotCloneAProtectedProject/state/state.json

@@ -0,0 +1,173 @@
+[
+	{
+		"project": "protected",
+		"getDoc": {
+            "error": 403,
+            "versionID": 1,
+            "createdAt": "2014-11-30T18:40:58Z",
+            "email": "jdleesmiller+1@gmail.com",
+            "name": "John+1"
+		},
+		"getSavedVers": [
+        	{
+        		"versionID": 1,
+        		"comment": "added more info on doc GET and error details",
+        		"email": "jdleesmiller+1@gmail.com",
+        		"name": "John+1",
+        		"createdAt": "2014-11-30T18:47:01Z"
+        	}
+		],
+		"getForVers": [
+			{
+				"versionID": 1,
+				"srcs": [
+                    {
+                    	"content": "contentchanged\n",
+                    	"path": "main.tex"
+                    },
+                    {
+                    	"content": "This text is from another file.",
+                    	"path": "foo/bar/test.tex"
+                    }
+	            ],
+	            "atts": []
+	        }
+		],
+		"push": "success",
+		"postback": {
+			"type": "outOfDate"
+		}
+	},
+	{
+		"project": "invalidFiles",
+		"getDoc": {
+			"versionID": 1,
+			"createdAt": "2014-11-30T18:40:58Z",
+			"email": "jdleesmiller+1@gmail.com",
+			"name": "John+1"
+		},
+		"getSavedVers": [
+        	{
+        		"versionID": 1,
+        		"comment": "added more info on doc GET and error details",
+        		"email": "jdleesmiller+1@gmail.com",
+        		"name": "John+1",
+        		"createdAt": "2014-11-30T18:47:01Z"
+        	}
+		],
+		"getForVers": [
+			{
+				"versionID": 1,
+				"srcs": [
+                    {
+                    	"content": "changedñcontent\n",
+                    	"path": "main.tex"
+                    },
+                    {
+                    	"content": "This text is from another file.",
+                    	"path": "foo/bar/test.tex"
+                    }
+	            ],
+	            "atts": []
+	        }
+		],
+		"push": "success",
+		"postback": {
+			"type": "invalidFiles",
+			"errors": [
+				{
+					"file": "file.invalid",
+					"state": "error"
+				},
+				{
+					"file": "virus.exe",
+					"state": "disallowed"
+				},
+				{
+					"file": "my image.jpg",
+					"state": "unclean_name",
+					"cleanFile": "my_image.jpg"
+				}
+			]
+		}
+	},
+	{
+		"project": "invalidProject",
+		"getDoc": {
+			"versionID": 1,
+			"createdAt": "2014-11-30T18:40:58Z",
+			"email": "jdleesmiller+1@gmail.com",
+			"name": "John+1"
+		},
+		"getSavedVers": [
+        	{
+        		"versionID": 1,
+        		"comment": "added more info on doc GET and error details",
+        		"email": "jdleesmiller+1@gmail.com",
+        		"name": "John+1",
+        		"createdAt": "2014-11-30T18:47:01Z"
+        	}
+		],
+		"getForVers": [
+			{
+				"versionID": 1,
+				"srcs": [
+                    {
+                    	"content": "content\n",
+                    	"path": "main.tex"
+                    },
+                    {
+                    	"content": "This text is from another file.",
+                    	"path": "foo/bar/test.tex"
+                    }
+	            ],
+	            "atts": []
+	        }
+		],
+		"push": "success",
+		"postback": {
+			"type": "invalidProject",
+			"errors": [
+				"No main.tex file exists."
+			]
+		}
+	},
+	{
+		"project": "error",
+		"getDoc": {
+			"versionID": 1,
+			"createdAt": "2014-11-30T18:40:58Z",
+			"email": "jdleesmiller+1@gmail.com",
+			"name": "John+1"
+		},
+		"getSavedVers": [
+        	{
+        		"versionID": 1,
+        		"comment": "added more info on doc GET and error details",
+        		"email": "jdleesmiller+1@gmail.com",
+        		"name": "John+1",
+        		"createdAt": "2014-11-30T18:47:01Z"
+        	}
+		],
+		"getForVers": [
+			{
+				"versionID": 1,
+				"srcs": [
+                    {
+                    	"content": "content\n",
+                    	"path": "main.tex"
+                    },
+                    {
+                    	"content": "This text is from another file.",
+                    	"path": "foo/bar/test.tex"
+                    }
+	            ],
+	            "atts": []
+	        }
+		],
+		"push": "success",
+		"postback": {
+			"type": "error"
+		}
+	}
+]