Преглед на файлове

Federation support for Teams

tags/v0.8.0
James Moger преди 12 години
родител
ревизия
997c16d682

+ 6
- 4
docs/02_federation.mkd Целия файл

@@ -13,7 +13,9 @@ Please review all the documentation to understand how it works and its limitatio
### Important Changes to Note
The Gitblit 0.7.0 federation protocol is incompatible with the 0.6.0 federation protocol because of a change in the way timestamps are formatted.
The *Gitblit 0.8.0* federation protocol adds retrieval of team definitions. Older clients will not know to request team information.
The *Gitblit 0.7.0* federation protocol is incompatible with the 0.6.0 federation protocol because of a change in the way timestamps are formatted.
Gitblit 0.6.0 uses the default [google-gson](http://google-gson.googlecode.com) timestamp serializer which generates locally formatted timestamps. Unfortunately, this creates problems for distributed repositories and distributed developers. Gitblit 0.7.0 corrects this error by serializing dates to the [iso8601](http://en.wikipedia.org/wiki/ISO_8601) standard. As a result 0.7.0 is not compatible with 0.6.0. A partial backwards-compatibility fallback was considered but it would only work one direction and since the federation mechanism is bidirectional it was not implemented.
@@ -151,13 +153,13 @@ After a repository has been cloned it is flagged as *isFederated* (which identif
During a federated pull operation, Gitblit does check that the *origin* of the local repository starts with the url of the federation registration.
If they do not match, the repository is skipped and this is indicated in the log.
#### User Accounts
#### User Accounts & Teams
By default all user accounts except the *admin* account are automatically pulled when using the *ALL* token or the *USERS_AND_REPOSITORIES* token. You may exclude a user account from being pulled by a federated Gitblit instance by checking *exclude from federation* in the edit user page.
By default all user accounts and teams (except the *admin* account) are automatically pulled when using the *ALL* token or the *USERS_AND_REPOSITORIES* token. You may exclude a user account from being pulled by a federated Gitblit instance by checking *exclude from federation* in the edit user page.
The pulling Gitblit instance will store a registration-specific `users.conf` file for the pulled user accounts and their repository permissions. This file is stored in the *federation.N.folder* folder.
If you specify *federation.N.mergeAccounts=true*, then the user accounts from the origin Gitblit instance will be integrated into the `users.conf` file of your Gitblit instance and allow sign-on of those users.
If you specify *federation.N.mergeAccounts=true*, then the user accounts and team definitions from the origin Gitblit instance will be integrated into the `users.conf` file of your Gitblit instance and allow sign-on of those users.
**NOTE:**
Upgrades from older Gitblit versions will not have the *#notfederated* role assigned to the *admin* account. Without that role, your admin account WILL be transferred with an *ALL* or *USERS_AND_REPOSITORIES* token.

+ 1
- 1
src/com/gitblit/Constants.java Целия файл

@@ -117,7 +117,7 @@ public class Constants {
* Enumeration representing the types of federation requests.
*/
public static enum FederationRequest {
POKE, PROPOSAL, PULL_REPOSITORIES, PULL_USERS, PULL_SETTINGS, STATUS;
POKE, PROPOSAL, PULL_REPOSITORIES, PULL_USERS, PULL_TEAMS, PULL_SETTINGS, STATUS;
public static FederationRequest fromName(String name) {
for (FederationRequest type : values()) {

+ 30
- 2
src/com/gitblit/FederationPullExecutor.java Целия файл

@@ -47,6 +47,7 @@ import com.gitblit.Constants.FederationStrategy;
import com.gitblit.GitBlitException.ForbiddenException;
import com.gitblit.models.FederationModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.FederationUtils;
import com.gitblit.utils.JGitUtils;
@@ -282,10 +283,12 @@ public class FederationPullExecutor implements Runnable {
try {
// Pull USERS
// TeamModels are automatically pulled because they are contained
// within the UserModel. The UserService creates unknown teams
// and updates existing teams.
Collection<UserModel> users = FederationUtils.getUsers(registration);
if (users != null && users.size() > 0) {
File realmFile = new File(registrationFolderFile, registration.name
+ "_users.conf");
File realmFile = new File(registrationFolderFile, registration.name + "_users.conf");
realmFile.delete();
ConfigUserService userService = new ConfigUserService(realmFile);
for (UserModel user : users) {
@@ -318,6 +321,31 @@ public class FederationPullExecutor implements Runnable {
localUser.canAdmin = user.canAdmin;
GitBlit.self().updateUserModel(localUser.username, localUser, false);
}
for (String teamname : GitBlit.self().getAllTeamnames()) {
TeamModel team = GitBlit.self().getTeamModel(teamname);
if (user.isTeamMember(teamname) && !team.hasUser(user.username)) {
// new team member
team.addUser(user.username);
GitBlit.self().updateTeamModel(teamname, team, false);
} else if (!user.isTeamMember(teamname) && team.hasUser(user.username)) {
// remove team member
team.removeUser(user.username);
GitBlit.self().updateTeamModel(teamname, team, false);
}
// update team repositories
TeamModel remoteTeam = user.getTeam(teamname);
if (remoteTeam != null && remoteTeam.repositories != null) {
int before = team.repositories.size();
team.addRepositories(remoteTeam.repositories);
int after = team.repositories.size();
if (after > before) {
// repository count changed, update
GitBlit.self().updateTeamModel(teamname, team, false);
}
}
}
}
}
}

+ 19
- 1
src/com/gitblit/FederationServlet.java Целия файл

@@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletResponse;
import com.gitblit.Constants.FederationRequest;
import com.gitblit.models.FederationModel;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.FederationUtils;
import com.gitblit.utils.HttpUtils;
@@ -90,7 +91,7 @@ public class FederationServlet extends JsonServlet {
if (proposal == null) {
return;
}
// reject proposal, if not receipt prohibited
if (!GitBlit.getBoolean(Keys.federation.allowProposals, false)) {
logger.error(MessageFormat.format("Rejected {0} federation proposal from {1}",
@@ -198,6 +199,23 @@ public class FederationServlet extends JsonServlet {
}
}
result = users;
} else if (FederationRequest.PULL_TEAMS.equals(reqType)) {
// pull teams
if (!GitBlit.self().validateFederationRequest(reqType, token)) {
// invalid token to pull teams
logger.warn(MessageFormat.format(
"Federation token from {0} not authorized to pull TEAMS",
request.getRemoteAddr()));
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
List<String> teamnames = GitBlit.self().getAllTeamnames();
List<TeamModel> teams = new ArrayList<TeamModel>();
for (String teamname : teamnames) {
TeamModel user = GitBlit.self().getTeamModel(teamname);
teams.add(user);
}
result = teams;
}
}

+ 12
- 0
src/com/gitblit/models/UserModel.java Целия файл

@@ -96,6 +96,18 @@ public class UserModel implements Principal, Serializable, Comparable<UserModel>
return false;
}
public TeamModel getTeam(String teamname) {
if (teams == null) {
return null;
}
for (TeamModel team : teams) {
if (team.name.equalsIgnoreCase(teamname)) {
return team;
}
}
return null;
}
@Override
public String getName() {
return username;

+ 18
- 0
src/com/gitblit/utils/FederationUtils.java Целия файл

@@ -38,6 +38,7 @@ import com.gitblit.Keys;
import com.gitblit.models.FederationModel;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.google.gson.reflect.TypeToken;
@@ -58,6 +59,9 @@ public class FederationUtils {
private static final Type USERS_TYPE = new TypeToken<Collection<UserModel>>() {
}.getType();
private static final Type TEAMS_TYPE = new TypeToken<Collection<TeamModel>>() {
}.getType();
private static final Logger LOGGER = LoggerFactory.getLogger(FederationUtils.class);
/**
@@ -280,6 +284,20 @@ public class FederationUtils {
return list;
}
/**
* Tries to pull the gitblit team definitions from the remote gitblit instance.
*
* @param registration
* @return a collection of TeamModel objects
* @throws Exception
*/
public static List<TeamModel> getTeams(FederationModel registration) throws Exception {
String url = asLink(registration.url, registration.token, FederationRequest.PULL_TEAMS);
Collection<TeamModel> models = JsonUtils.retrieveJson(url, TEAMS_TYPE);
List<TeamModel> list = new ArrayList<TeamModel>(models);
return list;
}
/**
* Tries to pull the gitblit server settings from the remote gitblit
* instance.

+ 74
- 10
tests/com/gitblit/tests/FederationTests.java Целия файл

@@ -16,10 +16,12 @@
package com.gitblit.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -31,16 +33,21 @@ import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.FederationProposalResult;
import com.gitblit.Constants.FederationRequest;
import com.gitblit.Constants.FederationToken;
import com.gitblit.models.FederationModel;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.FederationUtils;
import com.gitblit.utils.JsonUtils;
import com.gitblit.utils.RpcUtils;
public class FederationTests {
String url = GitBlitSuite.url;
String account = GitBlitSuite.account;
String password = GitBlitSuite.password;
String token = "d7cc58921a80b37e0329a4dae2f9af38bf61ef5c";
private static final AtomicBoolean started = new AtomicBoolean(false);
@@ -80,16 +87,73 @@ public class FederationTests {
FederationProposalResult.NO_PROPOSALS);
}
@Test
public void testJsonRepositories() throws Exception {
String requrl = FederationUtils.asLink(url, token, FederationRequest.PULL_REPOSITORIES);
String json = JsonUtils.retrieveJsonString(requrl, null, null);
assertNotNull(json);
}
@Test
public void testJsonUsers() throws Exception {
String requrl = FederationUtils.asLink(url, token, FederationRequest.PULL_USERS);
String json = JsonUtils.retrieveJsonString(requrl, null, null);
assertNotNull(json);
}
@Test
public void testJsonTeams() throws Exception {
String requrl = FederationUtils.asLink(url, token, FederationRequest.PULL_TEAMS);
String json = JsonUtils.retrieveJsonString(requrl, null, null);
assertNotNull(json);
}
private FederationModel getRegistration() {
FederationModel model = new FederationModel("localhost");
model.url = this.url;
model.token = this.token;
return model;
}
@Test
public void testPullRepositories() throws Exception {
try {
String requrl = FederationUtils.asLink(url, "d7cc58921a80b37e0329a4dae2f9af38bf61ef5c",
FederationRequest.PULL_REPOSITORIES);
String json = JsonUtils.retrieveJsonString(requrl, null, null);
} catch (IOException e) {
if (!e.getMessage().contains("403")) {
throw e;
}
}
Map<String, RepositoryModel> repos = FederationUtils.getRepositories(getRegistration(),
false);
assertNotNull(repos);
assertTrue(repos.size() > 0);
}
@Test
public void testPullUsers() throws Exception {
List<UserModel> users = FederationUtils.getUsers(getRegistration());
assertNotNull(users);
// admin is excluded
assertEquals(0, users.size());
UserModel newUser = new UserModel("test");
newUser.password = "whocares";
assertTrue(RpcUtils.createUser(newUser, url, account, password.toCharArray()));
TeamModel team = new TeamModel("testteam");
team.addUser("test");
team.addRepository("helloworld.git");
assertTrue(RpcUtils.createTeam(team, url, account, password.toCharArray()));
users = FederationUtils.getUsers(getRegistration());
assertNotNull(users);
assertEquals(1, users.size());
newUser = users.get(0);
assertTrue(newUser.isTeamMember("testteam"));
assertTrue(RpcUtils.deleteUser(newUser, url, account, password.toCharArray()));
assertTrue(RpcUtils.deleteTeam(team, url, account, password.toCharArray()));
}
@Test
public void testPullTeams() throws Exception {
List<TeamModel> teams = FederationUtils.getTeams(getRegistration());
assertNotNull(teams);
assertTrue(teams.size() > 0);
}
}

Loading…
Отказ
Запис