@@ -11,9 +11,11 @@ r24: { | |||
security: ~ | |||
fixes: ~ | |||
changes: ~ | |||
additions: ~ | |||
additions: | |||
- Add FORK_REPOSITORY RPC request type (issue-371, pr-161, ticket-65) | |||
dependencyChanges: ~ | |||
contributors: ~ | |||
contributors: | |||
- Manisha Gayathri | |||
} | |||
# |
@@ -350,9 +350,10 @@ public class Constants { | |||
* a client. | |||
*/ | |||
public static enum RpcRequest { | |||
// Order is important here. anything above LIST_SETTINGS requires | |||
// Order is important here. anything after LIST_SETTINGS requires | |||
// administrator privileges and web.allowRpcManagement. | |||
CLEAR_REPOSITORY_CACHE, REINDEX_TICKETS, GET_PROTOCOL, LIST_REPOSITORIES, LIST_BRANCHES, GET_USER, LIST_SETTINGS, | |||
CLEAR_REPOSITORY_CACHE, REINDEX_TICKETS, GET_PROTOCOL, LIST_REPOSITORIES, LIST_BRANCHES, GET_USER, | |||
FORK_REPOSITORY, LIST_SETTINGS, | |||
CREATE_REPOSITORY, EDIT_REPOSITORY, DELETE_REPOSITORY, | |||
LIST_USERS, CREATE_USER, EDIT_USER, DELETE_USER, | |||
LIST_TEAMS, CREATE_TEAM, EDIT_TEAM, DELETE_TEAM, |
@@ -53,13 +53,12 @@ import dagger.ObjectGraph; | |||
* Handles remote procedure calls. | |||
* | |||
* @author James Moger | |||
* | |||
*/ | |||
public class RpcServlet extends JsonServlet { | |||
private static final long serialVersionUID = 1L; | |||
public static final int PROTOCOL_VERSION = 7; | |||
public static final int PROTOCOL_VERSION = 8; | |||
private IStoredSettings settings; | |||
@@ -80,12 +79,11 @@ public class RpcServlet extends JsonServlet { | |||
* @throws java.io.IOException | |||
*/ | |||
@Override | |||
protected void processRequest(HttpServletRequest request, HttpServletResponse response) | |||
throws ServletException, IOException { | |||
protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, | |||
IOException { | |||
RpcRequest reqType = RpcRequest.fromName(request.getParameter("req")); | |||
String objectName = request.getParameter("name"); | |||
logger.info(MessageFormat.format("Rpc {0} request from {1}", reqType, | |||
request.getRemoteAddr())); | |||
logger.info(MessageFormat.format("Rpc {0} request from {1}", reqType, request.getRemoteAddr())); | |||
UserModel user = (UserModel) request.getUserPrincipal(); | |||
@@ -130,7 +128,8 @@ public class RpcServlet extends JsonServlet { | |||
} | |||
if (model.isCollectingGarbage) { | |||
// skip garbage collecting repository | |||
logger.warn(MessageFormat.format("Temporarily excluding {0} from RPC, busy collecting garbage", model.name)); | |||
logger.warn(MessageFormat.format("Temporarily excluding {0} from RPC, busy collecting garbage", | |||
model.name)); | |||
continue; | |||
} | |||
// get local branches | |||
@@ -196,6 +195,33 @@ public class RpcServlet extends JsonServlet { | |||
} catch (GitBlitException e) { | |||
response.setStatus(failureCode); | |||
} | |||
} else if (RpcRequest.FORK_REPOSITORY.equals(reqType)) { | |||
// fork repository | |||
RepositoryModel origin = gitblit.getRepositoryModel(objectName); | |||
if (origin == null) { | |||
// failed to find repository, error is logged by the repository | |||
// manager | |||
response.setStatus(failureCode); | |||
} else { | |||
if (user == null || !user.canFork(origin)) { | |||
logger.error("User {} is not permitted to fork '{}'!", user == null ? "anonymous" : user.username, | |||
objectName); | |||
response.setStatus(failureCode); | |||
} else { | |||
try { | |||
// fork the origin | |||
RepositoryModel fork = gitblit.fork(origin, user); | |||
if (fork == null) { | |||
logger.error("Failed to fork repository '{}'!", objectName); | |||
response.setStatus(failureCode); | |||
} else { | |||
logger.info("User {} has forked '{}'!", user.username, objectName); | |||
} | |||
} catch (GitBlitException e) { | |||
response.setStatus(failureCode); | |||
} | |||
} | |||
} | |||
} else if (RpcRequest.EDIT_REPOSITORY.equals(reqType)) { | |||
// edit repository | |||
RepositoryModel model = deserialize(request, response, RepositoryModel.class); | |||
@@ -281,7 +307,8 @@ public class RpcServlet extends JsonServlet { | |||
} else if (RpcRequest.SET_REPOSITORY_MEMBER_PERMISSIONS.equals(reqType)) { | |||
// set the repository permissions for the specified users | |||
RepositoryModel model = gitblit.getRepositoryModel(objectName); | |||
Collection<RegistrantAccessPermission> permissions = deserialize(request, response, RpcUtils.REGISTRANT_PERMISSIONS_TYPE); | |||
Collection<RegistrantAccessPermission> permissions = deserialize(request, response, | |||
RpcUtils.REGISTRANT_PERMISSIONS_TYPE); | |||
result = gitblit.setUserAccessPermissions(model, permissions); | |||
} else if (RpcRequest.LIST_REPOSITORY_TEAMS.equals(reqType)) { | |||
// get repository teams | |||
@@ -297,7 +324,8 @@ public class RpcServlet extends JsonServlet { | |||
} else if (RpcRequest.SET_REPOSITORY_TEAM_PERMISSIONS.equals(reqType)) { | |||
// set the repository permissions for the specified teams | |||
RepositoryModel model = gitblit.getRepositoryModel(objectName); | |||
Collection<RegistrantAccessPermission> permissions = deserialize(request, response, RpcUtils.REGISTRANT_PERMISSIONS_TYPE); | |||
Collection<RegistrantAccessPermission> permissions = deserialize(request, response, | |||
RpcUtils.REGISTRANT_PERMISSIONS_TYPE); | |||
result = gitblit.setTeamAccessPermissions(model, permissions); | |||
} else if (RpcRequest.LIST_FEDERATION_REGISTRATIONS.equals(reqType)) { | |||
// return the list of federation registrations | |||
@@ -363,8 +391,7 @@ public class RpcServlet extends JsonServlet { | |||
} else if (RpcRequest.EDIT_SETTINGS.equals(reqType)) { | |||
// update settings on the server | |||
if (allowAdmin) { | |||
Map<String, String> map = deserialize(request, response, | |||
RpcUtils.SETTINGS_TYPE); | |||
Map<String, String> map = deserialize(request, response, RpcUtils.SETTINGS_TYPE); | |||
gitblit.updateSettings(map); | |||
} else { | |||
response.sendError(notAllowedCode); |
@@ -203,7 +203,21 @@ public class RpcUtils { | |||
} | |||
/** | |||
/** | |||
* Create a fork of a repository. | |||
* | |||
* @param repository | |||
* @return true if the action succeeded | |||
* @throws IOException | |||
*/ | |||
public static boolean forkRepository(RepositoryModel repository, String serverUrl, | |||
String account, char[] password) throws IOException { | |||
return doAction(RpcRequest.FORK_REPOSITORY, repository.name, null, serverUrl, account, password); | |||
} | |||
/** | |||
* Send a revised version of the repository model to the Gitblit server. | |||
* | |||
* @param repository |
@@ -57,9 +57,10 @@ The Gitblit API includes methods for retrieving and interpreting RSS feeds. The | |||
<tr><td>Gitblit v0.8.0</td><td>2</td></tr> | |||
<tr><td>Gitblit v0.9.0 - v1.0.0</td><td>3</td></tr> | |||
<tr><td>Gitblit v1.1.0</td><td>4</td></tr> | |||
<tr><td>Gitblit v1.2.0+</td><td>5</td></tr> | |||
<tr><td>Gitblit v1.3.1+</td><td>6</td></tr> | |||
<tr><td>Gitblit v1.4.0+</td><td>7</td></tr> | |||
<tr><td>Gitblit v1.2.0</td><td>5</td></tr> | |||
<tr><td>Gitblit v1.3.1</td><td>6</td></tr> | |||
<tr><td>Gitblit v1.4.0</td><td>7</td></tr> | |||
<tr><td>Gitblit v1.6.0</td><td>8</td></tr> | |||
</tbody> | |||
</table> | |||
@@ -81,6 +82,7 @@ Use *SET_REPOSITORY_TEAM_PERMISSIONS* instead. | |||
<tr><td>LIST_BRANCHES</td><td>-</td><td>-</td><td>1</td><td>-</td><td>Map<String, List<String>></td></tr> | |||
<tr><td>LIST_SETTINGS</td><td>-</td><td><em>-</em></td><td>1</td><td>-</td><td>ServerSettings (basic keys)</td></tr> | |||
<tr><td>GET_USER</td><td>user name</td><td>-</td><td>6</td><td>-</td><td>UserModel</td></tr> | |||
<tr><td>FORK_REPOSITORY</td><td>repository name</td><td><em>-</em></td><td>8</td><td>-</td><td>-</td></tr> | |||
<tr><td colspan='6'><em>web.enableRpcManagement=true</em></td></tr> | |||
<tr><td>CREATE_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>1</td><td>RepositoryModel</td><td>-</td></tr> | |||
<tr><td>EDIT_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>1</td><td>RepositoryModel</td><td>-</td></tr> |
@@ -397,4 +397,67 @@ public class RpcTests extends GitblitUnitTest { | |||
assertNotNull(branches); | |||
assertTrue(branches.size() > 0); | |||
} | |||
@Test | |||
public void testFork() throws Exception { | |||
// test forking by an administrator | |||
// admins are all-powerful and can fork the unforakable :) | |||
testFork(account, password, true, true); | |||
testFork(account, password, false, true); | |||
// test forking by a permitted normal user | |||
UserModel forkUser = new UserModel("forkuser"); | |||
forkUser.password = forkUser.username; | |||
forkUser.canFork = true; | |||
RpcUtils.deleteUser(forkUser, url, account, password.toCharArray()); | |||
RpcUtils.createUser(forkUser, url, account, password.toCharArray()); | |||
testFork(forkUser.username, forkUser.password, true, true); | |||
testFork(forkUser.username, forkUser.password, false, false); | |||
RpcUtils.deleteUser(forkUser, url, account, password.toCharArray()); | |||
// test forking by a non-permitted normal user | |||
UserModel noForkUser = new UserModel("noforkuser"); | |||
noForkUser.password = noForkUser.username; | |||
noForkUser.canFork = false; | |||
RpcUtils.deleteUser(noForkUser, url, account, password.toCharArray()); | |||
RpcUtils.createUser(noForkUser, url, account, password.toCharArray()); | |||
testFork(forkUser.username, forkUser.password, true, false); | |||
testFork(forkUser.username, forkUser.password, false, false); | |||
RpcUtils.deleteUser(noForkUser, url, account, password.toCharArray()); | |||
} | |||
private void testFork(String forkAcct, String forkAcctPassword, boolean allowForks, boolean expectSuccess) throws Exception { | |||
// test does not exist | |||
RepositoryModel dne = new RepositoryModel(); | |||
dne.name = "doesNotExist.git"; | |||
assertFalse(String.format("Successfully forked %s!", dne.name), | |||
RpcUtils.forkRepository(dne, url, forkAcct, forkAcctPassword.toCharArray())); | |||
// delete any previous fork | |||
RepositoryModel fork = findRepository(String.format("~%s/helloworld.git", forkAcct)); | |||
if (fork != null) { | |||
RpcUtils.deleteRepository(fork, url, account, password.toCharArray()); | |||
} | |||
// update the origin to allow forks or not | |||
RepositoryModel origin = findRepository("helloworld.git"); | |||
origin.allowForks = allowForks; | |||
RpcUtils.updateRepository(origin.name, origin, url, account, password.toCharArray()); | |||
// fork the repository | |||
if (expectSuccess) { | |||
assertTrue(String.format("Failed to fork %s!", origin.name), | |||
RpcUtils.forkRepository(origin, url, forkAcct, forkAcctPassword.toCharArray())); | |||
} else { | |||
assertFalse(String.format("Successfully forked %s!", origin.name), | |||
RpcUtils.forkRepository(origin, url, forkAcct, forkAcctPassword.toCharArray())); | |||
} | |||
// attempt another fork | |||
assertFalse(String.format("Successfully forked %s!", origin.name), | |||
RpcUtils.forkRepository(origin, url, forkAcct, forkAcctPassword.toCharArray())); | |||
// delete the fork repository | |||
RpcUtils.deleteRepository(fork, url, account, password.toCharArray()); | |||
} | |||
} |