security: ~ | security: ~ | ||||
fixes: ~ | fixes: ~ | ||||
changes: ~ | changes: ~ | ||||
additions: ~ | |||||
additions: | |||||
- Add FORK_REPOSITORY RPC request type (issue-371, pr-161, ticket-65) | |||||
dependencyChanges: ~ | dependencyChanges: ~ | ||||
contributors: ~ | |||||
contributors: | |||||
- Manisha Gayathri | |||||
} | } | ||||
# | # |
* a client. | * a client. | ||||
*/ | */ | ||||
public static enum RpcRequest { | 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. | // administrator privileges and web.allowRpcManagement. | ||||
CLEAR_REPOSITORY_CACHE, REINDEX_TICKETS, GET_PROTOCOL, LIST_REPOSITORIES, LIST_BRANCHES, GET_USER, LIST_SETTINGS, | |||||
CREATE_REPOSITORY, FORK_REPOSITORY, EDIT_REPOSITORY, DELETE_REPOSITORY, | |||||
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_USERS, CREATE_USER, EDIT_USER, DELETE_USER, | ||||
LIST_TEAMS, CREATE_TEAM, EDIT_TEAM, DELETE_TEAM, | LIST_TEAMS, CREATE_TEAM, EDIT_TEAM, DELETE_TEAM, | ||||
LIST_REPOSITORY_MEMBERS, SET_REPOSITORY_MEMBERS, LIST_REPOSITORY_TEAMS, SET_REPOSITORY_TEAMS, | LIST_REPOSITORY_MEMBERS, SET_REPOSITORY_MEMBERS, LIST_REPOSITORY_TEAMS, SET_REPOSITORY_TEAMS, |
package com.gitblit.models; | |||||
/* | |||||
* Copyright 2011 gitblit.com. | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
import com.gitblit.utils.StringUtils; | |||||
import java.io.Serializable; | |||||
public class UserRepositoryCompositeModel implements Serializable { | |||||
private static final long serialVersionUID = 1L; | |||||
public UserModel userModel; | |||||
public RepositoryModel repositoryModel; | |||||
public UserModel getUserModel() { | |||||
return userModel; | |||||
} | |||||
public void setUserModel(UserModel userModel) { | |||||
this.userModel = userModel; | |||||
} | |||||
public RepositoryModel getRepositoryModel() { | |||||
return repositoryModel; | |||||
} | |||||
public void setRepositoryModel(RepositoryModel repositoryModel) { | |||||
this.repositoryModel = repositoryModel; | |||||
} | |||||
} |
import javax.servlet.http.HttpServletRequest; | import javax.servlet.http.HttpServletRequest; | ||||
import javax.servlet.http.HttpServletResponse; | import javax.servlet.http.HttpServletResponse; | ||||
import com.gitblit.models.*; | |||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
import com.gitblit.Constants; | import com.gitblit.Constants; | ||||
import com.gitblit.IStoredSettings; | import com.gitblit.IStoredSettings; | ||||
import com.gitblit.Keys; | import com.gitblit.Keys; | ||||
import com.gitblit.manager.IGitblit; | import com.gitblit.manager.IGitblit; | ||||
import com.gitblit.models.RefModel; | |||||
import com.gitblit.models.RegistrantAccessPermission; | |||||
import com.gitblit.models.RepositoryModel; | |||||
import com.gitblit.models.ServerSettings; | |||||
import com.gitblit.models.TeamModel; | |||||
import com.gitblit.models.UserModel; | |||||
import com.gitblit.utils.DeepCopier; | import com.gitblit.utils.DeepCopier; | ||||
import com.gitblit.utils.HttpUtils; | import com.gitblit.utils.HttpUtils; | ||||
import com.gitblit.utils.JGitUtils; | import com.gitblit.utils.JGitUtils; | ||||
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
public static final int PROTOCOL_VERSION = 7; | |||||
public static final int PROTOCOL_VERSION = 8; | |||||
private IStoredSettings settings; | private IStoredSettings settings; | ||||
response.setStatus(failureCode); | response.setStatus(failureCode); | ||||
} | } | ||||
} else if (RpcRequest.FORK_REPOSITORY.equals(reqType)) { | } else if (RpcRequest.FORK_REPOSITORY.equals(reqType)) { | ||||
// fork repository | |||||
UserRepositoryCompositeModel userRepositoryCompositeModel = deserialize(request, response, | |||||
UserRepositoryCompositeModel.class); | |||||
RepositoryModel repoModel = userRepositoryCompositeModel.getRepositoryModel(); | |||||
UserModel userModel = userRepositoryCompositeModel.getUserModel(); | |||||
try { | |||||
if (repoModel != null && userModel != null) { | |||||
gitblit.fork(repoModel, userModel); | |||||
} else { | |||||
System.out.println("Non existing user model or repo model"); | |||||
response.setStatus(failureCode); | |||||
} | |||||
} catch (GitBlitException e) { | |||||
response.setStatus(failureCode); | |||||
} | |||||
// 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)) { | } else if (RpcRequest.EDIT_REPOSITORY.equals(reqType)) { | ||||
// edit repository | // edit repository | ||||
RepositoryModel model = deserialize(request, response, RepositoryModel.class); | RepositoryModel model = deserialize(request, response, RepositoryModel.class); |
import com.gitblit.Constants; | import com.gitblit.Constants; | ||||
import com.gitblit.Constants.RpcRequest; | import com.gitblit.Constants.RpcRequest; | ||||
import com.gitblit.GitBlitException.UnknownRequestException; | import com.gitblit.GitBlitException.UnknownRequestException; | ||||
import com.gitblit.models.*; | |||||
import com.gitblit.models.FederationModel; | |||||
import com.gitblit.models.FederationProposal; | |||||
import com.gitblit.models.FederationSet; | |||||
import com.gitblit.models.FeedModel; | |||||
import com.gitblit.models.RegistrantAccessPermission; | |||||
import com.gitblit.models.RepositoryModel; | |||||
import com.gitblit.models.ServerSettings; | |||||
import com.gitblit.models.ServerStatus; | |||||
import com.gitblit.models.TeamModel; | |||||
import com.gitblit.models.UserModel; | |||||
import com.google.gson.reflect.TypeToken; | import com.google.gson.reflect.TypeToken; | ||||
/** | /** | ||||
} | } | ||||
/** | /** | ||||
* Create a fork of an already existing repo | |||||
* Create a fork of a repository. | |||||
* | * | ||||
* @param repository | * @param repository | ||||
* @param user | |||||
* @return true if the action succeeded | * @return true if the action succeeded | ||||
* @throws IOException | * @throws IOException | ||||
*/ | */ | ||||
public static boolean forkRpository(RepositoryModel repository, UserModel user, String serverUrl, | |||||
public static boolean forkRepository(RepositoryModel repository, String serverUrl, | |||||
String account, char[] password) throws IOException { | String account, char[] password) throws IOException { | ||||
UserRepositoryCompositeModel userRepositoryCompositeModel = new UserRepositoryCompositeModel(); | |||||
userRepositoryCompositeModel.setRepositoryModel(repository); | |||||
userRepositoryCompositeModel.setUserModel(user); | |||||
return doAction(RpcRequest.FORK_REPOSITORY, null, userRepositoryCompositeModel, serverUrl, account, password); | |||||
return doAction(RpcRequest.FORK_REPOSITORY, repository.name, null, serverUrl, account, password); | |||||
} | } | ||||
<tr><td>Gitblit v0.8.0</td><td>2</td></tr> | <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 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.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> | </tbody> | ||||
</table> | </table> | ||||
<tr><td>LIST_BRANCHES</td><td>-</td><td>-</td><td>1</td><td>-</td><td>Map<String, List<String>></td></tr> | <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>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>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 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>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> | <tr><td>EDIT_REPOSITORY</td><td>repository name</td><td><em>admin</em></td><td>1</td><td>RepositoryModel</td><td>-</td></tr> |
role = "#notfederated" | role = "#notfederated" | ||||
[user "sampleuser"] | [user "sampleuser"] | ||||
password = sampleuser | password = sampleuser | ||||
cookie = 6e07ed42149fc166206319faffdfba2e2ec82e43 | |||||
accountType = LOCAL | accountType = LOCAL | ||||
role = "#none" | role = "#none" | ||||
[team "admins"] | [team "admins"] |
assertEquals(AccessRestrictionType.VIEW, retrievedRepository.accessRestriction); | assertEquals(AccessRestrictionType.VIEW, retrievedRepository.accessRestriction); | ||||
assertEquals(AuthorizationControl.AUTHENTICATED, retrievedRepository.authorizationControl); | assertEquals(AuthorizationControl.AUTHENTICATED, retrievedRepository.authorizationControl); | ||||
//fork repo | |||||
UserModel userModel = new UserModel("garbageUser"); | |||||
assertTrue("Failed to create Fork Repository!", | |||||
RpcUtils.forkRpository(model, userModel, url, account, password.toCharArray())); | |||||
// rename and change access restriciton | // rename and change access restriciton | ||||
String originalName = model.name; | String originalName = model.name; | ||||
model.name = "garbagerepo2.git"; | model.name = "garbagerepo2.git"; | ||||
assertNotNull(branches); | assertNotNull(branches); | ||||
assertTrue(branches.size() > 0); | 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()); | |||||
} | |||||
} | } |
package com.gitblit.tests; | |||||
/** | |||||
* Created with IntelliJ IDEA. | |||||
* User: manisha | |||||
* Date: 3/21/14 | |||||
* Time: 12:03 PM | |||||
* To change this template use File | Settings | File Templates. | |||||
*/ | |||||
public @interface Test { | |||||
} |