From ac50c26aeffa12ccbc435603ced0cff1d60e51d9 Mon Sep 17 00:00:00 2001 From: Winston Li Date: Fri, 20 Feb 2015 12:38:12 +0000 Subject: [PATCH] (fix #8) Add better error message for 403 protected projects with integration test and support. --- .../bridge/WriteLatexDataSource.java | 3 +- .../git/handler/WLRepositoryResolver.java | 1 - .../test/state/SnapshotAPIState.java | 2 +- .../test/state/SnapshotAPIStateBuilder.java | 3 +- .../writelatex/SnapshotFetcher.java | 8 +- .../wlgitbridge/writelatex/WriteLatexAPI.java | 5 +- .../request/getdoc/SnapshotGetDocResult.java | 54 ++++-- .../exception/ProtectedProjectException.java | 29 +++ .../WLGitBridgeIntegrationTest.java | 32 ++++ .../state/state.json | 173 ++++++++++++++++++ 10 files changed, 291 insertions(+), 19 deletions(-) create mode 100644 services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/api/request/getdoc/exception/ProtectedProjectException.java create mode 100644 services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/cannotCloneAProtectedProject/state/state.json diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/WriteLatexDataSource.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/WriteLatexDataSource.java index 075f923dcf..c249d3717b 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/bridge/WriteLatexDataSource.java +++ b/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 getWritableRepositories(String projectName) throws FailedConnectionException, InvalidProjectException; public void putDirectoryContentsToProjectWithName(String projectName, RawDirectoryContents directoryContents, String hostname) throws SnapshotPostException, IOException, FailedConnectionException; diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLRepositoryResolver.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLRepositoryResolver.java index 7ec5caa615..2c5f26b627 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLRepositoryResolver.java +++ b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLRepositoryResolver.java @@ -41,7 +41,6 @@ public class WLRepositoryResolver implements RepositoryResolver(); - 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(); List savedVers = new LinkedList(); diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/test/state/SnapshotAPIStateBuilder.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/test/state/SnapshotAPIStateBuilder.java index 7e5e4d53a0..fc034222df 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/test/state/SnapshotAPIStateBuilder.java +++ b/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())); diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/SnapshotFetcher.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/SnapshotFetcher.java index cb40d583b9..65bdd64e54 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/SnapshotFetcher.java +++ b/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 fetchedIDs, Map 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; } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/WriteLatexAPI.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/WriteLatexAPI.java index f3a6b24e95..f72d33845d 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/WriteLatexAPI.java +++ b/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); } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/api/request/getdoc/SnapshotGetDocResult.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/api/request/getdoc/SnapshotGetDocResult.java index 93f2ce0f44..1f76d76bb7 100644 --- a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/api/request/getdoc/SnapshotGetDocResult.java +++ b/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; } diff --git a/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/api/request/getdoc/exception/ProtectedProjectException.java b/services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/writelatex/api/request/getdoc/exception/ProtectedProjectException.java new file mode 100644 index 0000000000..059d99f8c6 --- /dev/null +++ b/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 getDescriptionLines() { + return Arrays.asList("You can't currently clone a protected project."); + } + + @Override + public void fromJSON(JsonElement json) { + + } + +} diff --git a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest.java b/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest.java index 41ba162366..15a629a218 100644 --- a/services/git-bridge/src/test/java/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest.java +++ b/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() {{ + 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(); diff --git a/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/cannotCloneAProtectedProject/state/state.json b/services/git-bridge/src/test/resources/uk/ac/ic/wlgitbridge/WLGitBridgeIntegrationTest/cannotCloneAProtectedProject/state/state.json new file mode 100644 index 0000000000..4599d38d0e --- /dev/null +++ b/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" + } + } +] \ No newline at end of file