From 23e08cdfd5f61e06f584c7fce4e765dd8b6e6643 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Fri, 29 Nov 2013 11:05:51 -0500
Subject: [PATCH] Refactor managers and authentication for federation

---
 src/main/java/com/gitblit/git/GitDaemon.java                 |    4 
 src/main/java/com/gitblit/service/FederationPullService.java |   13 
 src/main/java/com/gitblit/manager/UserManager.java           |   14 
 src/main/java/com/gitblit/GitBlit.java                       |  993 -------------------------
 src/main/java/com/gitblit/manager/IFederationManager.java    |   17 
 src/main/java/com/gitblit/servlet/FederationServlet.java     |    6 
 src/main/java/com/gitblit/servlet/GitFilter.java             |   28 
 src/main/java/com/gitblit/DaggerModule.java                  |    2 
 src/main/java/com/gitblit/FederationClient.java              |   48 +
 src/main/java/com/gitblit/manager/GitblitManager.java        | 1096 ++++++++++++++++++++++++++++
 src/main/java/com/gitblit/manager/ServicesManager.java       |    9 
 src/main/java/com/gitblit/manager/AuthenticationManager.java |   30 
 src/main/java/com/gitblit/manager/FederationManager.java     |   43 +
 build.xml                                                    |    3 
 build.moxie                                                  |    4 
 src/main/java/com/gitblit/manager/IUserManager.java          |    7 
 16 files changed, 1,273 insertions(+), 1,044 deletions(-)

diff --git a/build.moxie b/build.moxie
index acc1afb..b09c7a5 100644
--- a/build.moxie
+++ b/build.moxie
@@ -129,7 +129,7 @@
 - compile 'log4j:log4j:1.2.17' :war :fedclient :authority
 - compile 'org.slf4j:slf4j-api:1.6.6' :war :fedclient :authority
 - compile 'org.slf4j:slf4j-log4j12:1.6.6' :war :fedclient :authority
-- compile 'javax.mail:mail:1.4.3' :war :fedclient :authority
+- compile 'javax.mail:mail:1.4.3' :war :authority
 - compile 'javax.servlet:javax.servlet-api:3.0.1' :fedclient
 - compile 'org.eclipse.jetty.aggregate:jetty-webapp:${jetty.version}' @jar
 - compile 'org.eclipse.jetty:jetty-ajp:${jetty.version}' @jar
@@ -148,7 +148,7 @@
 - compile 'org.fusesource.wikitext:mediawiki-core:${wikitext.version}' :war
 - compile 'org.fusesource.wikitext:confluence-core:${wikitext.version}' :war
 - compile 'org.eclipse.jgit:org.eclipse.jgit:${jgit.version}' :war :fedclient :manager :authority
-- compile 'org.eclipse.jgit:org.eclipse.jgit.http.server:${jgit.version}' :war :fedclient :manager :authority
+- compile 'org.eclipse.jgit:org.eclipse.jgit.http.server:${jgit.version}' :war :manager :authority
 - compile 'org.bouncycastle:bcprov-jdk15on:${bouncycastle.version}' :war :authority
 - compile 'org.bouncycastle:bcmail-jdk15on:${bouncycastle.version}' :war :authority
 - compile 'org.bouncycastle:bcpkix-jdk15on:${bouncycastle.version}' :war :authority
diff --git a/build.xml b/build.xml
index ab6699b..eb97ceb 100644
--- a/build.xml
+++ b/build.xml
@@ -279,7 +279,8 @@
 		<!-- generate jar by traversing the class hierarchy of the specified
 			 classes, exclude any classes in classpath jars -->
 		<mx:genjar tag="" includeresources="false" excludeClasspathJars="true"
-			destfile="${project.targetDirectory}/fedclient.jar">
+			destfile="${project.targetDirectory}/fedclient.jar"
+			excludes="**/.class,**/*.java, **/Thumbs.db, **/*.mkd, com/gitblit/wicket/**">
 			<mainclass name="com.gitblit.FederationClient" />
 			<class name="com.gitblit.Keys" />
 			<launcher paths="ext" />
diff --git a/src/main/java/com/gitblit/DaggerModule.java b/src/main/java/com/gitblit/DaggerModule.java
index 135fafb..857f36a 100644
--- a/src/main/java/com/gitblit/DaggerModule.java
+++ b/src/main/java/com/gitblit/DaggerModule.java
@@ -149,13 +149,11 @@
 	@Provides @Singleton IFederationManager provideFederationManager(
 			IRuntimeManager runtimeManager,
 			INotificationManager notificationManager,
-			IUserManager userManager,
 			IRepositoryManager repositoryManager) {
 
 		return new FederationManager(
 				runtimeManager,
 				notificationManager,
-				userManager,
 				repositoryManager);
 	}
 
diff --git a/src/main/java/com/gitblit/FederationClient.java b/src/main/java/com/gitblit/FederationClient.java
index 66b378a..c275746 100644
--- a/src/main/java/com/gitblit/FederationClient.java
+++ b/src/main/java/com/gitblit/FederationClient.java
@@ -17,6 +17,7 @@
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 import com.beust.jcommander.JCommander;
@@ -24,7 +25,9 @@
 import com.beust.jcommander.ParameterException;
 import com.beust.jcommander.Parameters;
 import com.gitblit.manager.FederationManager;
-import com.gitblit.manager.NotificationManager;
+import com.gitblit.manager.GitblitManager;
+import com.gitblit.manager.IGitblit;
+import com.gitblit.manager.INotificationManager;
 import com.gitblit.manager.RepositoryManager;
 import com.gitblit.manager.RuntimeManager;
 import com.gitblit.manager.UserManager;
@@ -89,14 +92,14 @@
 		}
 
 		// configure the Gitblit singleton for minimal, non-server operation
-		RuntimeManager runtime = new RuntimeManager(settings);
-		runtime.setBaseFolder(baseFolder);
-		NotificationManager notifications = new NotificationManager(settings).start();
+		RuntimeManager runtime = new RuntimeManager(settings, baseFolder).start();
+		NoopNotificationManager notifications = new NoopNotificationManager().start();
 		UserManager users = new UserManager(runtime).start();
 		RepositoryManager repositories = new RepositoryManager(runtime, users).start();
-		FederationManager federation = new FederationManager(runtime, notifications, users, repositories).start();
+		FederationManager federation = new FederationManager(runtime, notifications, repositories).start();
+		IGitblit gitblit = new GitblitManager(runtime, notifications, users, null, repositories, null, federation);
 
-		FederationPullService puller = new FederationPullService(federation.getFederationRegistrations()) {
+		FederationPullService puller = new FederationPullService(gitblit, federation.getFederationRegistrations()) {
 			@Override
 			public void reschedule(FederationModel registration) {
 				// NOOP
@@ -153,4 +156,37 @@
 		public String repositoriesFolder;
 
 	}
+
+	private static class NoopNotificationManager implements INotificationManager {
+
+		@Override
+		public NoopNotificationManager start() {
+			return this;
+		}
+
+		@Override
+		public NoopNotificationManager stop() {
+			return this;
+		}
+
+		@Override
+		public void sendMailToAdministrators(String subject, String message) {
+		}
+
+		@Override
+		public void sendMail(String subject, String message, Collection<String> toAddresses) {
+		}
+
+		@Override
+		public void sendMail(String subject, String message, String... toAddresses) {
+		}
+
+		@Override
+		public void sendHtmlMail(String subject, String message, Collection<String> toAddresses) {
+		}
+
+		@Override
+		public void sendHtmlMail(String subject, String message, String... toAddresses) {
+		}
+	}
 }
diff --git a/src/main/java/com/gitblit/GitBlit.java b/src/main/java/com/gitblit/GitBlit.java
index d07ac9a..da80a74 100644
--- a/src/main/java/com/gitblit/GitBlit.java
+++ b/src/main/java/com/gitblit/GitBlit.java
@@ -15,98 +15,35 @@
  */
 package com.gitblit;
 
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.lang.reflect.Type;
 import java.text.MessageFormat;
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
 import java.util.List;
-import java.util.Map;
-import java.util.TimeZone;
 
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jgit.lib.Repository;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.gitblit.Constants.AccessPermission;
-import com.gitblit.Constants.AccessRestrictionType;
-import com.gitblit.Constants.FederationRequest;
-import com.gitblit.Constants.FederationToken;
+import com.gitblit.manager.GitblitManager;
 import com.gitblit.manager.IAuthenticationManager;
 import com.gitblit.manager.IFederationManager;
-import com.gitblit.manager.IGitblit;
 import com.gitblit.manager.INotificationManager;
 import com.gitblit.manager.IProjectManager;
 import com.gitblit.manager.IRepositoryManager;
 import com.gitblit.manager.IRuntimeManager;
 import com.gitblit.manager.IUserManager;
 import com.gitblit.manager.ServicesManager;
-import com.gitblit.models.FederationModel;
-import com.gitblit.models.FederationProposal;
-import com.gitblit.models.FederationSet;
-import com.gitblit.models.ForkModel;
-import com.gitblit.models.GitClientApplication;
-import com.gitblit.models.Metric;
-import com.gitblit.models.ProjectModel;
-import com.gitblit.models.RegistrantAccessPermission;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.RepositoryUrl;
-import com.gitblit.models.SearchResult;
-import com.gitblit.models.ServerSettings;
-import com.gitblit.models.ServerStatus;
-import com.gitblit.models.SettingModel;
-import com.gitblit.models.TeamModel;
 import com.gitblit.models.UserModel;
-import com.gitblit.utils.ArrayUtils;
-import com.gitblit.utils.HttpUtils;
-import com.gitblit.utils.JGitUtils;
-import com.gitblit.utils.JsonUtils;
-import com.gitblit.utils.ObjectCache;
 import com.gitblit.utils.StringUtils;
-import com.google.gson.Gson;
-import com.google.gson.JsonIOException;
-import com.google.gson.JsonSyntaxException;
-import com.google.gson.reflect.TypeToken;
 
 /**
- * GitBlit is an aggregate interface delegate.  It implements all the manager
- * interfaces and delegates all methods calls to the actual manager implementations.
- * It's primary purpose is to provide complete management control to the git
- * upload and receive pack functions.
+ * GitBlit is the aggregate manager for the Gitblit webapp.  It provides all
+ * management functions and also manages some long-running services.
  *
  * @author James Moger
  *
  */
-public class GitBlit implements IGitblit {
-
-	private final Logger logger = LoggerFactory.getLogger(getClass());
-
-	private final ObjectCache<Collection<GitClientApplication>> clientApplications = new ObjectCache<Collection<GitClientApplication>>();
-
-	private final IStoredSettings settings;
-
-	private final IRuntimeManager runtimeManager;
-
-	private final INotificationManager notificationManager;
-
-	private final IUserManager userManager;
-
-	private final IAuthenticationManager authenticationManager;
-
-	private final IRepositoryManager repositoryManager;
-
-	private final IProjectManager projectManager;
-
-	private final IFederationManager federationManager;
+public class GitBlit extends GitblitManager {
 
 	private final ServicesManager servicesManager;
 
@@ -119,21 +56,20 @@
 			IProjectManager projectManager,
 			IFederationManager federationManager) {
 
-		this.settings = runtimeManager.getSettings();
-		this.runtimeManager = runtimeManager;
-		this.notificationManager = notificationManager;
-		this.userManager = userManager;
-		this.authenticationManager = authenticationManager;
-		this.repositoryManager = repositoryManager;
-		this.projectManager = projectManager;
-		this.federationManager = federationManager;
+		super(runtimeManager,
+				notificationManager,
+				userManager,
+				authenticationManager,
+				repositoryManager,
+				projectManager,
+				federationManager);
 
 		this.servicesManager = new ServicesManager(this);
 	}
 
 	@Override
 	public GitBlit start() {
-		loadSettingModels(runtimeManager.getSettingsModel());
+		super.start();
 		logger.info("Starting services manager...");
 		servicesManager.start();
 		return this;
@@ -141,167 +77,9 @@
 
 	@Override
 	public GitBlit stop() {
+		super.stop();
 		servicesManager.stop();
 		return this;
-	}
-
-	/*
-	 * IGITBLIT
-	 */
-
-	/**
-	 * Creates a personal fork of the specified repository. The clone is view
-	 * restricted by default and the owner of the source repository is given
-	 * access to the clone.
-	 *
-	 * @param repository
-	 * @param user
-	 * @return the repository model of the fork, if successful
-	 * @throws GitBlitException
-	 */
-	@Override
-	public RepositoryModel fork(RepositoryModel repository, UserModel user) throws GitBlitException {
-		String cloneName = MessageFormat.format("{0}/{1}.git", user.getPersonalPath(), StringUtils.stripDotGit(StringUtils.getLastPathElement(repository.name)));
-		String fromUrl = MessageFormat.format("file://{0}/{1}", repositoryManager.getRepositoriesFolder().getAbsolutePath(), repository.name);
-
-		// clone the repository
-		try {
-			JGitUtils.cloneRepository(repositoryManager.getRepositoriesFolder(), cloneName, fromUrl, true, null);
-		} catch (Exception e) {
-			throw new GitBlitException(e);
-		}
-
-		// create a Gitblit repository model for the clone
-		RepositoryModel cloneModel = repository.cloneAs(cloneName);
-		// owner has REWIND/RW+ permissions
-		cloneModel.addOwner(user.username);
-		repositoryManager.updateRepositoryModel(cloneName, cloneModel, false);
-
-		// add the owner of the source repository to the clone's access list
-		if (!ArrayUtils.isEmpty(repository.owners)) {
-			for (String owner : repository.owners) {
-				UserModel originOwner = userManager.getUserModel(owner);
-				if (originOwner != null) {
-					originOwner.setRepositoryPermission(cloneName, AccessPermission.CLONE);
-					reviseUser(originOwner.username, originOwner);
-				}
-			}
-		}
-
-		// grant origin's user list clone permission to fork
-		List<String> users = repositoryManager.getRepositoryUsers(repository);
-		List<UserModel> cloneUsers = new ArrayList<UserModel>();
-		for (String name : users) {
-			if (!name.equalsIgnoreCase(user.username)) {
-				UserModel cloneUser = userManager.getUserModel(name);
-				if (cloneUser.canClone(repository)) {
-					// origin user can clone origin, grant clone access to fork
-					cloneUser.setRepositoryPermission(cloneName, AccessPermission.CLONE);
-				}
-				cloneUsers.add(cloneUser);
-			}
-		}
-		userManager.updateUserModels(cloneUsers);
-
-		// grant origin's team list clone permission to fork
-		List<String> teams = repositoryManager.getRepositoryTeams(repository);
-		List<TeamModel> cloneTeams = new ArrayList<TeamModel>();
-		for (String name : teams) {
-			TeamModel cloneTeam = userManager.getTeamModel(name);
-			if (cloneTeam.canClone(repository)) {
-				// origin team can clone origin, grant clone access to fork
-				cloneTeam.setRepositoryPermission(cloneName, AccessPermission.CLONE);
-			}
-			cloneTeams.add(cloneTeam);
-		}
-		userManager.updateTeamModels(cloneTeams);
-
-		// add this clone to the cached model
-		repositoryManager.addToCachedRepositoryList(cloneModel);
-		return cloneModel;
-	}
-
-	/**
-	 * Adds a TeamModel object.
-	 *
-	 * @param team
-	 */
-	@Override
-	public void addTeam(TeamModel team) throws GitBlitException {
-		if (!userManager.updateTeamModel(team)) {
-			throw new GitBlitException("Failed to add team!");
-		}
-	}
-
-	/**
-	 * Updates the TeamModel object for the specified name.
-	 *
-	 * @param teamname
-	 * @param team
-	 */
-	@Override
-	public void reviseTeam(String teamname, TeamModel team) throws GitBlitException {
-		if (!teamname.equalsIgnoreCase(team.name)) {
-			if (userManager.getTeamModel(team.name) != null) {
-				throw new GitBlitException(MessageFormat.format(
-						"Failed to rename ''{0}'' because ''{1}'' already exists.", teamname,
-						team.name));
-			}
-		}
-		if (!userManager.updateTeamModel(teamname, team)) {
-			throw new GitBlitException("Failed to update team!");
-		}
-	}
-
-	/**
-	 * Adds a user object.
-	 *
-	 * @param user
-	 * @throws GitBlitException
-	 */
-	@Override
-	public void addUser(UserModel user) throws GitBlitException {
-		if (!userManager.updateUserModel(user)) {
-			throw new GitBlitException("Failed to add user!");
-		}
-	}
-
-	/**
-	 * Updates a user object keyed by username. This method allows
-	 * for renaming a user.
-	 *
-	 * @param username
-	 * @param user
-	 * @throws GitBlitException
-	 */
-	@Override
-	public void reviseUser(String username, UserModel user) throws GitBlitException {
-		if (!username.equalsIgnoreCase(user.username)) {
-			if (userManager.getUserModel(user.username) != null) {
-				throw new GitBlitException(MessageFormat.format(
-						"Failed to rename ''{0}'' because ''{1}'' already exists.", username,
-						user.username));
-			}
-
-			// rename repositories and owner fields for all repositories
-			for (RepositoryModel model : repositoryManager.getRepositoryModels(user)) {
-				if (model.isUsersPersonalRepository(username)) {
-					// personal repository
-					model.addOwner(user.username);
-					String oldRepositoryName = model.name;
-					model.name = user.getPersonalPath() + model.name.substring(model.projectPath.length());
-					model.projectPath = user.getPersonalPath();
-					repositoryManager.updateRepositoryModel(oldRepositoryName, model, false);
-				} else if (model.isOwner(username)) {
-					// common/shared repo
-					model.addOwner(user.username);
-					repositoryManager.updateRepositoryModel(model.name, model, false);
-				}
-			}
-		}
-		if (!userManager.updateUserModel(username, user)) {
-			throw new GitBlitException("Failed to update user!");
-		}
 	}
 
 	/**
@@ -352,750 +130,5 @@
 			}
 		}
 		return list;
-	}
-
-	protected String getRepositoryUrl(HttpServletRequest request, String username, RepositoryModel repository) {
-		StringBuilder sb = new StringBuilder();
-		sb.append(HttpUtils.getGitblitURL(request));
-		sb.append(Constants.R_PATH);
-		sb.append(repository.name);
-
-		// inject username into repository url if authentication is required
-		if (repository.accessRestriction.exceeds(AccessRestrictionType.NONE)
-				&& !StringUtils.isEmpty(username)) {
-			sb.insert(sb.indexOf("://") + 3, username + "@");
-		}
-		return sb.toString();
-	}
-
-
-	/**
-	 * Returns the list of custom client applications to be used for the
-	 * repository url panel;
-	 *
-	 * @return a collection of client applications
-	 */
-	@Override
-	public Collection<GitClientApplication> getClientApplications() {
-		// prefer user definitions, if they exist
-		File userDefs = new File(runtimeManager.getBaseFolder(), "clientapps.json");
-		if (userDefs.exists()) {
-			Date lastModified = new Date(userDefs.lastModified());
-			if (clientApplications.hasCurrent("user", lastModified)) {
-				return clientApplications.getObject("user");
-			} else {
-				// (re)load user definitions
-				try {
-					InputStream is = new FileInputStream(userDefs);
-					Collection<GitClientApplication> clients = readClientApplications(is);
-					is.close();
-					if (clients != null) {
-						clientApplications.updateObject("user", lastModified, clients);
-						return clients;
-					}
-				} catch (IOException e) {
-					logger.error("Failed to deserialize " + userDefs.getAbsolutePath(), e);
-				}
-			}
-		}
-
-		// no user definitions, use system definitions
-		if (!clientApplications.hasCurrent("system", new Date(0))) {
-			try {
-				InputStream is = getClass().getResourceAsStream("/clientapps.json");
-				Collection<GitClientApplication> clients = readClientApplications(is);
-				is.close();
-				if (clients != null) {
-					clientApplications.updateObject("system", new Date(0), clients);
-				}
-			} catch (IOException e) {
-				logger.error("Failed to deserialize clientapps.json resource!", e);
-			}
-		}
-
-		return clientApplications.getObject("system");
-	}
-
-	private Collection<GitClientApplication> readClientApplications(InputStream is) {
-		try {
-			Type type = new TypeToken<Collection<GitClientApplication>>() {
-			}.getType();
-			InputStreamReader reader = new InputStreamReader(is);
-			Gson gson = JsonUtils.gson();
-			Collection<GitClientApplication> links = gson.fromJson(reader, type);
-			return links;
-		} catch (JsonIOException e) {
-			logger.error("Error deserializing client applications!", e);
-		} catch (JsonSyntaxException e) {
-			logger.error("Error deserializing client applications!", e);
-		}
-		return null;
-	}
-
-	/**
-	 * Parse the properties file and aggregate all the comments by the setting
-	 * key. A setting model tracks the current value, the default value, the
-	 * description of the setting and and directives about the setting.
-	 *
-	 * @return Map<String, SettingModel>
-	 */
-	private void loadSettingModels(ServerSettings settingsModel) {
-		try {
-			// Read bundled Gitblit properties to extract setting descriptions.
-			// This copy is pristine and only used for populating the setting
-			// models map.
-			InputStream is = getClass().getResourceAsStream("/reference.properties");
-			BufferedReader propertiesReader = new BufferedReader(new InputStreamReader(is));
-			StringBuilder description = new StringBuilder();
-			SettingModel setting = new SettingModel();
-			String line = null;
-			while ((line = propertiesReader.readLine()) != null) {
-				if (line.length() == 0) {
-					description.setLength(0);
-					setting = new SettingModel();
-				} else {
-					if (line.charAt(0) == '#') {
-						if (line.length() > 1) {
-							String text = line.substring(1).trim();
-							if (SettingModel.CASE_SENSITIVE.equals(text)) {
-								setting.caseSensitive = true;
-							} else if (SettingModel.RESTART_REQUIRED.equals(text)) {
-								setting.restartRequired = true;
-							} else if (SettingModel.SPACE_DELIMITED.equals(text)) {
-								setting.spaceDelimited = true;
-							} else if (text.startsWith(SettingModel.SINCE)) {
-								try {
-									setting.since = text.split(" ")[1];
-								} catch (Exception e) {
-									setting.since = text;
-								}
-							} else {
-								description.append(text);
-								description.append('\n');
-							}
-						}
-					} else {
-						String[] kvp = line.split("=", 2);
-						String key = kvp[0].trim();
-						setting.name = key;
-						setting.defaultValue = kvp[1].trim();
-						setting.currentValue = setting.defaultValue;
-						setting.description = description.toString().trim();
-						settingsModel.add(setting);
-						description.setLength(0);
-						setting = new SettingModel();
-					}
-				}
-			}
-			propertiesReader.close();
-		} catch (NullPointerException e) {
-			logger.error("Failed to find resource copy of gitblit.properties");
-		} catch (IOException e) {
-			logger.error("Failed to load resource copy of gitblit.properties");
-		}
-	}
-
-	/*
-	 * ISTOREDSETTINGS
-	 *
-	 * these methods are necessary for (nearly) seamless Groovy hook operation
-	 * after the massive refactor.
-	 */
-
-	public boolean getBoolean(String key, boolean defaultValue) {
-		return runtimeManager.getSettings().getBoolean(key, defaultValue);
-	}
-
-	public String getString(String key, String defaultValue) {
-		return runtimeManager.getSettings().getString(key, defaultValue);
-	}
-
-	public int getInteger(String key, int defaultValue) {
-		return runtimeManager.getSettings().getInteger(key, defaultValue);
-	}
-
-	public List<String> getStrings(String key) {
-		return runtimeManager.getSettings().getStrings(key);
-	}
-
-	/*
-	 * RUNTIME MANAGER
-	 */
-
-	@Override
-	public File getBaseFolder() {
-		return runtimeManager.getBaseFolder();
-	}
-
-	@Override
-	public void setBaseFolder(File folder) {
-		runtimeManager.setBaseFolder(folder);
-	}
-
-	@Override
-	public Date getBootDate() {
-		return runtimeManager.getBootDate();
-	}
-
-	@Override
-	public ServerSettings getSettingsModel() {
-		return runtimeManager.getSettingsModel();
-	}
-
-	@Override
-	public boolean isServingRepositories() {
-		return runtimeManager.isServingRepositories();
-	}
-
-	@Override
-	public TimeZone getTimezone() {
-		return runtimeManager.getTimezone();
-	}
-
-	@Override
-	public boolean isDebugMode() {
-		return runtimeManager.isDebugMode();
-	}
-
-	@Override
-	public File getFileOrFolder(String key, String defaultFileOrFolder) {
-		return runtimeManager.getFileOrFolder(key, defaultFileOrFolder);
-	}
-
-	@Override
-	public File getFileOrFolder(String fileOrFolder) {
-		return runtimeManager.getFileOrFolder(fileOrFolder);
-	}
-
-	@Override
-	public IStoredSettings getSettings() {
-		return runtimeManager.getSettings();
-	}
-
-	@Override
-	public boolean updateSettings(Map<String, String> updatedSettings) {
-		return runtimeManager.updateSettings(updatedSettings);
-	}
-
-	@Override
-	public ServerStatus getStatus() {
-		return runtimeManager.getStatus();
-	}
-
-	/*
-	 * NOTIFICATION MANAGER
-	 */
-
-	@Override
-	public void sendMailToAdministrators(String subject, String message) {
-		notificationManager.sendMailToAdministrators(subject, message);
-	}
-
-	@Override
-	public void sendMail(String subject, String message, Collection<String> toAddresses) {
-		notificationManager.sendMail(subject, message, toAddresses);
-	}
-
-	@Override
-	public void sendMail(String subject, String message, String... toAddresses) {
-		notificationManager.sendMail(subject, message, toAddresses);
-	}
-
-	@Override
-	public void sendHtmlMail(String subject, String message, Collection<String> toAddresses) {
-		notificationManager.sendHtmlMail(subject, message, toAddresses);
-	}
-
-	@Override
-	public void sendHtmlMail(String subject, String message, String... toAddresses) {
-		notificationManager.sendHtmlMail(subject, message, toAddresses);
-	}
-
-	/*
-	 * SESSION MANAGER
-	 */
-
-	@Override
-	public UserModel authenticate(String username, char[] password) {
-		return authenticationManager.authenticate(username, password);
-	}
-
-	@Override
-	public UserModel authenticate(HttpServletRequest httpRequest) {
-		return authenticationManager.authenticate(httpRequest, false);
-	}
-	@Override
-	public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) {
-		return authenticationManager.authenticate(httpRequest, requiresCertificate);
-	}
-
-	@Override
-	public void setCookie(HttpServletResponse response, UserModel user) {
-		authenticationManager.setCookie(response, user);
-	}
-
-	@Override
-	public void logout(HttpServletResponse response, UserModel user) {
-		authenticationManager.logout(response, user);
-	}
-
-	@Override
-	public boolean supportsCredentialChanges(UserModel user) {
-		return authenticationManager.supportsCredentialChanges(user);
-	}
-
-	@Override
-	public boolean supportsDisplayNameChanges(UserModel user) {
-		return authenticationManager.supportsDisplayNameChanges(user);
-	}
-
-	@Override
-	public boolean supportsEmailAddressChanges(UserModel user) {
-		return authenticationManager.supportsEmailAddressChanges(user);
-	}
-
-	@Override
-	public boolean supportsTeamMembershipChanges(UserModel user) {
-		return authenticationManager.supportsTeamMembershipChanges(user);
-	}
-
-	@Override
-	public boolean supportsTeamMembershipChanges(TeamModel team) {
-		return authenticationManager.supportsTeamMembershipChanges(team);
-	}
-
-	/*
-	 * USER MANAGER
-	 */
-
-	@Override
-	public void setup(IRuntimeManager runtimeManager) {
-	}
-
-	@Override
-	public List<String> getAllUsernames() {
-		return userManager.getAllUsernames();
-	}
-
-	@Override
-	public List<UserModel> getAllUsers() {
-		return userManager.getAllUsers();
-	}
-
-	@Override
-	public boolean deleteUser(String username) {
-		return userManager.deleteUser(username);
-	}
-
-	@Override
-	public UserModel getUserModel(String username) {
-		return userManager.getUserModel(username);
-	}
-
-	@Override
-	public List<TeamModel> getAllTeams() {
-		return userManager.getAllTeams();
-	}
-
-	@Override
-	public TeamModel getTeamModel(String teamname) {
-		return userManager.getTeamModel(teamname);
-	}
-
-	@Override
-	public String getCookie(UserModel model) {
-		return userManager.getCookie(model);
-	}
-
-	@Override
-	public UserModel getUserModel(char[] cookie) {
-		return userManager.getUserModel(cookie);
-	}
-
-	@Override
-	public boolean updateUserModel(UserModel model) {
-		return userManager.updateUserModel(model);
-	}
-
-	@Override
-	public boolean updateUserModels(Collection<UserModel> models) {
-		return userManager.updateUserModels(models);
-	}
-
-	@Override
-	public boolean updateUserModel(String username, UserModel model) {
-		return userManager.updateUserModel(username, model);
-	}
-
-	@Override
-	public boolean deleteUserModel(UserModel model) {
-		return userManager.deleteUserModel(model);
-	}
-
-	@Override
-	public List<String> getAllTeamNames() {
-		return userManager.getAllTeamNames();
-	}
-
-	@Override
-	public List<String> getTeamNamesForRepositoryRole(String role) {
-		return userManager.getTeamNamesForRepositoryRole(role);
-	}
-
-	@Override
-	public boolean updateTeamModel(TeamModel model) {
-		return userManager.updateTeamModel(model);
-	}
-
-	@Override
-	public boolean updateTeamModels(Collection<TeamModel> models) {
-		return userManager.updateTeamModels(models);
-	}
-
-	@Override
-	public boolean updateTeamModel(String teamname, TeamModel model) {
-		return userManager.updateTeamModel(teamname, model);
-	}
-
-	@Override
-	public boolean deleteTeamModel(TeamModel model) {
-		return userManager.deleteTeamModel(model);
-	}
-
-	@Override
-	public List<String> getUsernamesForRepositoryRole(String role) {
-		return userManager.getUsernamesForRepositoryRole(role);
-	}
-
-	@Override
-	public boolean renameRepositoryRole(String oldRole, String newRole) {
-		return userManager.renameRepositoryRole(oldRole, newRole);
-	}
-
-	@Override
-	public boolean deleteRepositoryRole(String role) {
-		return userManager.deleteRepositoryRole(role);
-	}
-
-	@Override
-	public boolean deleteTeam(String teamname) {
-		return userManager.deleteTeam(teamname);
-	}
-
-	/*
-	 * REPOSITORY MANAGER
-	 */
-
-	@Override
-	public Date getLastActivityDate() {
-		return repositoryManager.getLastActivityDate();
-	}
-
-	@Override
-	public File getRepositoriesFolder() {
-		return repositoryManager.getRepositoriesFolder();
-	}
-
-	@Override
-	public File getHooksFolder() {
-		return repositoryManager.getHooksFolder();
-	}
-
-	@Override
-	public File getGrapesFolder() {
-		return repositoryManager.getGrapesFolder();
-	}
-
-	@Override
-	public List<RegistrantAccessPermission> getUserAccessPermissions(UserModel user) {
-		return repositoryManager.getUserAccessPermissions(user);
-	}
-
-	@Override
-	public List<RegistrantAccessPermission> getUserAccessPermissions(RepositoryModel repository) {
-		return repositoryManager.getUserAccessPermissions(repository);
-	}
-
-	@Override
-	public boolean setUserAccessPermissions(RepositoryModel repository, Collection<RegistrantAccessPermission> permissions) {
-		return repositoryManager.setUserAccessPermissions(repository, permissions);
-	}
-
-	@Override
-	public List<String> getRepositoryUsers(RepositoryModel repository) {
-		return repositoryManager.getRepositoryUsers(repository);
-	}
-
-	@Override
-	public List<RegistrantAccessPermission> getTeamAccessPermissions(RepositoryModel repository) {
-		return repositoryManager.getTeamAccessPermissions(repository);
-	}
-
-	@Override
-	public boolean setTeamAccessPermissions(RepositoryModel repository, Collection<RegistrantAccessPermission> permissions) {
-		return repositoryManager.setTeamAccessPermissions(repository, permissions);
-	}
-
-	@Override
-	public List<String> getRepositoryTeams(RepositoryModel repository) {
-		return repositoryManager.getRepositoryTeams(repository);
-	}
-	@Override
-	public void addToCachedRepositoryList(RepositoryModel model) {
-		repositoryManager.addToCachedRepositoryList(model);
-	}
-
-	@Override
-	public void resetRepositoryListCache() {
-		repositoryManager.resetRepositoryListCache();
-	}
-
-	@Override
-	public List<String> getRepositoryList() {
-		return repositoryManager.getRepositoryList();
-	}
-
-	@Override
-	public Repository getRepository(String repositoryName) {
-		return repositoryManager.getRepository(repositoryName);
-	}
-
-	@Override
-	public Repository getRepository(String repositoryName, boolean logError) {
-		return repositoryManager.getRepository(repositoryName, logError);
-	}
-
-	@Override
-	public List<RepositoryModel> getRepositoryModels(UserModel user) {
-		return repositoryManager.getRepositoryModels(user);
-	}
-
-	@Override
-	public RepositoryModel getRepositoryModel(UserModel user, String repositoryName) {
-		return repositoryManager.getRepositoryModel(repositoryName);
-	}
-
-	@Override
-	public RepositoryModel getRepositoryModel(String repositoryName) {
-		return repositoryManager.getRepositoryModel(repositoryName);
-	}
-
-	@Override
-	public long getStarCount(RepositoryModel repository) {
-		return repositoryManager.getStarCount(repository);
-	}
-
-	@Override
-	public boolean hasRepository(String repositoryName) {
-		return repositoryManager.hasRepository(repositoryName);
-	}
-
-	@Override
-	public boolean hasRepository(String repositoryName, boolean caseSensitiveCheck) {
-		return repositoryManager.hasRepository(repositoryName, caseSensitiveCheck);
-	}
-
-	@Override
-	public boolean hasFork(String username, String origin) {
-		return repositoryManager.hasFork(username, origin);
-	}
-
-	@Override
-	public String getFork(String username, String origin) {
-		return repositoryManager.getFork(username, origin);
-	}
-
-	@Override
-	public ForkModel getForkNetwork(String repository) {
-		return repositoryManager.getForkNetwork(repository);
-	}
-
-	@Override
-	public long updateLastChangeFields(Repository r, RepositoryModel model) {
-		return repositoryManager.updateLastChangeFields(r, model);
-	}
-
-	@Override
-	public List<Metric> getRepositoryDefaultMetrics(RepositoryModel model, Repository repository) {
-		return repositoryManager.getRepositoryDefaultMetrics(model, repository);
-	}
-
-	@Override
-	public void updateRepositoryModel(String repositoryName, RepositoryModel repository,
-			boolean isCreate) throws GitBlitException {
-		repositoryManager.updateRepositoryModel(repositoryName, repository, isCreate);
-	}
-
-	@Override
-	public void updateConfiguration(Repository r, RepositoryModel repository) {
-		repositoryManager.updateConfiguration(r, repository);
-	}
-
-	@Override
-	public boolean deleteRepositoryModel(RepositoryModel model) {
-		return repositoryManager.deleteRepositoryModel(model);
-	}
-
-	@Override
-	public boolean deleteRepository(String repositoryName) {
-		return repositoryManager.deleteRepository(repositoryName);
-	}
-
-	@Override
-	public List<String> getAllScripts() {
-		return repositoryManager.getAllScripts();
-	}
-
-	@Override
-	public List<String> getPreReceiveScriptsInherited(RepositoryModel repository) {
-		return repositoryManager.getPreReceiveScriptsInherited(repository);
-	}
-
-	@Override
-	public List<String> getPreReceiveScriptsUnused(RepositoryModel repository) {
-		return repositoryManager.getPreReceiveScriptsUnused(repository);
-	}
-
-	@Override
-	public List<String> getPostReceiveScriptsInherited(RepositoryModel repository) {
-		return repositoryManager.getPostReceiveScriptsInherited(repository);
-	}
-
-	@Override
-	public List<String> getPostReceiveScriptsUnused(RepositoryModel repository) {
-		return repositoryManager.getPostReceiveScriptsUnused(repository);
-	}
-
-	@Override
-	public List<SearchResult> search(String query, int page, int pageSize, List<String> repositories) {
-		return repositoryManager.search(query, page, pageSize, repositories);
-	}
-
-	@Override
-	public boolean isCollectingGarbage() {
-		return repositoryManager.isCollectingGarbage();
-	}
-
-	@Override
-	public boolean isCollectingGarbage(String repositoryName) {
-		return repositoryManager.isCollectingGarbage(repositoryName);
-	}
-
-	/*
-	 * PROJECT MANAGER
-	 */
-
-	@Override
-	public List<ProjectModel> getProjectModels(UserModel user, boolean includeUsers) {
-		return projectManager.getProjectModels(user, includeUsers);
-	}
-
-	@Override
-	public ProjectModel getProjectModel(String name, UserModel user) {
-		return projectManager.getProjectModel(name, user);
-	}
-
-	@Override
-	public ProjectModel getProjectModel(String name) {
-		return projectManager.getProjectModel(name);
-	}
-
-	@Override
-	public List<ProjectModel> getProjectModels(List<RepositoryModel> repositoryModels, boolean includeUsers) {
-		return projectManager.getProjectModels(repositoryModels, includeUsers);
-	}
-
-	/*
-	 * FEDERATION MANAGER
-	 */
-
-	@Override
-	public File getProposalsFolder() {
-		return federationManager.getProposalsFolder();
-	}
-
-	@Override
-	public UserModel getFederationUser() {
-		return federationManager.getFederationUser();
-	}
-
-	@Override
-	public boolean canFederate() {
-		return federationManager.canFederate();
-	}
-
-	@Override
-	public List<FederationModel> getFederationRegistrations() {
-		return federationManager.getFederationRegistrations();
-	}
-
-	@Override
-	public FederationModel getFederationRegistration(String url, String name) {
-		return federationManager.getFederationRegistration(url, name);
-	}
-
-	@Override
-	public List<FederationSet> getFederationSets(String gitblitUrl) {
-		return federationManager.getFederationSets(gitblitUrl);
-	}
-
-	@Override
-	public List<String> getFederationTokens() {
-		return federationManager.getFederationTokens();
-	}
-
-	@Override
-	public String getFederationToken(FederationToken type) {
-		return federationManager.getFederationToken(type);
-	}
-
-	@Override
-	public String getFederationToken(String value) {
-		return federationManager.getFederationToken(value);
-	}
-
-	@Override
-	public boolean validateFederationRequest(FederationRequest req, String token) {
-		return federationManager.validateFederationRequest(req, token);
-	}
-
-	@Override
-	public boolean acknowledgeFederationStatus(String identification, FederationModel registration) {
-		return federationManager.acknowledgeFederationStatus(identification, registration);
-	}
-
-	@Override
-	public List<FederationModel> getFederationResultRegistrations() {
-		return federationManager.getFederationResultRegistrations();
-	}
-
-	@Override
-	public boolean submitFederationProposal(FederationProposal proposal, String gitblitUrl) {
-		return federationManager.submitFederationProposal(proposal, gitblitUrl);
-	}
-
-	@Override
-	public List<FederationProposal> getPendingFederationProposals() {
-		return federationManager.getPendingFederationProposals();
-	}
-
-	@Override
-	public Map<String, RepositoryModel> getRepositories(String gitblitUrl, String token) {
-		return federationManager.getRepositories(gitblitUrl, token);
-	}
-
-	@Override
-	public FederationProposal createFederationProposal(String gitblitUrl, String token) {
-		return federationManager.createFederationProposal(gitblitUrl, token);
-	}
-
-	@Override
-	public FederationProposal getPendingFederationProposal(String token) {
-		return federationManager.getPendingFederationProposal(token);
-	}
-
-	@Override
-	public boolean deletePendingFederationProposal(FederationProposal proposal) {
-		return federationManager.deletePendingFederationProposal(proposal);
 	}
 }
diff --git a/src/main/java/com/gitblit/git/GitDaemon.java b/src/main/java/com/gitblit/git/GitDaemon.java
index 7273f38..d026b5e 100644
--- a/src/main/java/com/gitblit/git/GitDaemon.java
+++ b/src/main/java/com/gitblit/git/GitDaemon.java
@@ -67,9 +67,9 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.gitblit.GitBlit;
 import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
+import com.gitblit.manager.IGitblit;
 import com.gitblit.utils.StringUtils;
 
 /**
@@ -108,7 +108,7 @@
 
 	private ReceivePackFactory<GitDaemonClient> receivePackFactory;
 
-	public GitDaemon(GitBlit gitblit) {
+	public GitDaemon(IGitblit gitblit) {
 
 		IStoredSettings settings = gitblit.getSettings();
 		int port = settings.getInteger(Keys.git.daemonPort, 0);
diff --git a/src/main/java/com/gitblit/manager/AuthenticationManager.java b/src/main/java/com/gitblit/manager/AuthenticationManager.java
index 6e541c4..8c66ce5 100644
--- a/src/main/java/com/gitblit/manager/AuthenticationManager.java
+++ b/src/main/java/com/gitblit/manager/AuthenticationManager.java
@@ -183,7 +183,7 @@
 			if (principal != null) {
 				String username = principal.getName();
 				if (!StringUtils.isEmpty(username)) {
-					boolean internalAccount = isInternalAccount(username);
+					boolean internalAccount = userManager.isInternalAccount(username);
 					UserModel user = userManager.getUserModel(username);
 					if (user != null) {
 						// existing user
@@ -322,15 +322,6 @@
 			// can not authenticate empty password
 			return null;
 		}
-		// check to see if this is the federation user
-//		if (canFederate()) {
-//			if (usernameDecoded.equalsIgnoreCase(Constants.FEDERATION_USER)) {
-//				List<String> tokens = getFederationTokens();
-//				if (tokens.contains(pw)) {
-//					return getFederationUser();
-//				}
-//			}
-//		}
 
 		// try local authentication
 		UserModel user = userManager.getUserModel(usernameDecoded);
@@ -489,23 +480,4 @@
 		}
 		return AuthenticationProvider.NULL_PROVIDER;
 	}
-
-	/**
-	 * Returns true if the username represents an internal account
-	 *
-	 * @param username
-	 * @return true if the specified username represents an internal account
-	 */
-	protected boolean isInternalAccount(String username) {
-		return !StringUtils.isEmpty(username)
-				&& (username.equalsIgnoreCase(Constants.FEDERATION_USER)
-						|| username.equalsIgnoreCase(UserModel.ANONYMOUS.username));
-	}
-
-//	protected UserModel getFederationUser() {
-//		// the federation user is an administrator
-//		UserModel federationUser = new UserModel(Constants.FEDERATION_USER);
-//		federationUser.canAdmin = true;
-//		return federationUser;
-//	}
 }
diff --git a/src/main/java/com/gitblit/manager/FederationManager.java b/src/main/java/com/gitblit/manager/FederationManager.java
index 568e87f..95d38af 100644
--- a/src/main/java/com/gitblit/manager/FederationManager.java
+++ b/src/main/java/com/gitblit/manager/FederationManager.java
@@ -17,6 +17,7 @@
 
 import java.io.File;
 import java.io.FileFilter;
+import java.nio.charset.Charset;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -24,6 +25,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+
+import javax.servlet.http.HttpServletRequest;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -38,6 +41,7 @@
 import com.gitblit.models.FederationSet;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
+import com.gitblit.utils.Base64;
 import com.gitblit.utils.FederationUtils;
 import com.gitblit.utils.JsonUtils;
 import com.gitblit.utils.StringUtils;
@@ -69,7 +73,6 @@
 	public FederationManager(
 			IRuntimeManager runtimeManager,
 			INotificationManager notificationManager,
-			IUserManager userManager,
 			IRepositoryManager repositoryManager) {
 
 		this.settings = runtimeManager.getSettings();
@@ -100,6 +103,17 @@
 	}
 
 	@Override
+	public boolean canFederate() {
+		String passphrase = settings.getString(Keys.federation.passphrase, "");
+		return !StringUtils.isEmpty(passphrase);
+	}
+
+	/**
+	 * Returns the federation user account.
+	 *
+	 * @return the federation user account
+	 */
+	@Override
 	public UserModel getFederationUser() {
 		// the federation user is an administrator
 		UserModel federationUser = new UserModel(Constants.FEDERATION_USER);
@@ -108,9 +122,30 @@
 	}
 
 	@Override
-	public boolean canFederate() {
-		String passphrase = settings.getString(Keys.federation.passphrase, "");
-		return !StringUtils.isEmpty(passphrase);
+	public UserModel authenticate(HttpServletRequest httpRequest) {
+		if (canFederate()) {
+			// try to authenticate federation user for cloning
+			final String authorization = httpRequest.getHeader("Authorization");
+			if (authorization != null && authorization.startsWith("Basic")) {
+				// Authorization: Basic base64credentials
+				String base64Credentials = authorization.substring("Basic".length()).trim();
+				String credentials = new String(Base64.decode(base64Credentials),
+						Charset.forName("UTF-8"));
+				// credentials = username:password
+				final String[] values = credentials.split(":", 2);
+				if (values.length == 2) {
+					String username = StringUtils.decodeUsername(values[0]);
+					String password = values[1];
+					if (username.equalsIgnoreCase(Constants.FEDERATION_USER)) {
+						List<String> tokens = getFederationTokens();
+						if (tokens.contains(password)) {
+							return getFederationUser();
+						}
+					}
+				}
+			}
+		}
+		return null;
 	}
 
 	/**
diff --git a/src/main/java/com/gitblit/manager/GitblitManager.java b/src/main/java/com/gitblit/manager/GitblitManager.java
new file mode 100644
index 0000000..b391181
--- /dev/null
+++ b/src/main/java/com/gitblit/manager/GitblitManager.java
@@ -0,0 +1,1096 @@
+/*
+ * Copyright 2013 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.
+ */
+package com.gitblit.manager;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Type;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.lib.Repository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.Constants;
+import com.gitblit.Constants.AccessPermission;
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.Constants.FederationRequest;
+import com.gitblit.Constants.FederationToken;
+import com.gitblit.GitBlitException;
+import com.gitblit.IStoredSettings;
+import com.gitblit.Keys;
+import com.gitblit.models.FederationModel;
+import com.gitblit.models.FederationProposal;
+import com.gitblit.models.FederationSet;
+import com.gitblit.models.ForkModel;
+import com.gitblit.models.GitClientApplication;
+import com.gitblit.models.Metric;
+import com.gitblit.models.ProjectModel;
+import com.gitblit.models.RegistrantAccessPermission;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.RepositoryUrl;
+import com.gitblit.models.SearchResult;
+import com.gitblit.models.ServerSettings;
+import com.gitblit.models.ServerStatus;
+import com.gitblit.models.SettingModel;
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.HttpUtils;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.JsonUtils;
+import com.gitblit.utils.ObjectCache;
+import com.gitblit.utils.StringUtils;
+import com.google.gson.Gson;
+import com.google.gson.JsonIOException;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * GitblitManager is an aggregate interface delegate.  It implements all the manager
+ * interfaces and delegates most methods calls to the proper manager implementation.
+ * It's primary purpose is to provide complete management control to the git
+ * upload and receive pack functions.
+ *
+ * GitblitManager also implements several integration methods when it is required
+ * to manipulate several manages for one operation.
+ *
+ * @author James Moger
+ *
+ */
+public class GitblitManager implements IGitblit {
+
+	protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+	protected final ObjectCache<Collection<GitClientApplication>> clientApplications = new ObjectCache<Collection<GitClientApplication>>();
+
+	protected final IStoredSettings settings;
+
+	protected final IRuntimeManager runtimeManager;
+
+	protected final INotificationManager notificationManager;
+
+	protected final IUserManager userManager;
+
+	protected final IAuthenticationManager authenticationManager;
+
+	protected final IRepositoryManager repositoryManager;
+
+	protected final IProjectManager projectManager;
+
+	protected final IFederationManager federationManager;
+
+	public GitblitManager(
+			IRuntimeManager runtimeManager,
+			INotificationManager notificationManager,
+			IUserManager userManager,
+			IAuthenticationManager authenticationManager,
+			IRepositoryManager repositoryManager,
+			IProjectManager projectManager,
+			IFederationManager federationManager) {
+
+		this.settings = runtimeManager.getSettings();
+		this.runtimeManager = runtimeManager;
+		this.notificationManager = notificationManager;
+		this.userManager = userManager;
+		this.authenticationManager = authenticationManager;
+		this.repositoryManager = repositoryManager;
+		this.projectManager = projectManager;
+		this.federationManager = federationManager;
+	}
+
+	@Override
+	public GitblitManager start() {
+		loadSettingModels(runtimeManager.getSettingsModel());
+		return this;
+	}
+
+	@Override
+	public GitblitManager stop() {
+		return this;
+	}
+
+	/*
+	 * IGITBLIT
+	 */
+
+	/**
+	 * Creates a personal fork of the specified repository. The clone is view
+	 * restricted by default and the owner of the source repository is given
+	 * access to the clone.
+	 *
+	 * @param repository
+	 * @param user
+	 * @return the repository model of the fork, if successful
+	 * @throws GitBlitException
+	 */
+	@Override
+	public RepositoryModel fork(RepositoryModel repository, UserModel user) throws GitBlitException {
+		String cloneName = MessageFormat.format("{0}/{1}.git", user.getPersonalPath(), StringUtils.stripDotGit(StringUtils.getLastPathElement(repository.name)));
+		String fromUrl = MessageFormat.format("file://{0}/{1}", repositoryManager.getRepositoriesFolder().getAbsolutePath(), repository.name);
+
+		// clone the repository
+		try {
+			JGitUtils.cloneRepository(repositoryManager.getRepositoriesFolder(), cloneName, fromUrl, true, null);
+		} catch (Exception e) {
+			throw new GitBlitException(e);
+		}
+
+		// create a Gitblit repository model for the clone
+		RepositoryModel cloneModel = repository.cloneAs(cloneName);
+		// owner has REWIND/RW+ permissions
+		cloneModel.addOwner(user.username);
+		repositoryManager.updateRepositoryModel(cloneName, cloneModel, false);
+
+		// add the owner of the source repository to the clone's access list
+		if (!ArrayUtils.isEmpty(repository.owners)) {
+			for (String owner : repository.owners) {
+				UserModel originOwner = userManager.getUserModel(owner);
+				if (originOwner != null) {
+					originOwner.setRepositoryPermission(cloneName, AccessPermission.CLONE);
+					reviseUser(originOwner.username, originOwner);
+				}
+			}
+		}
+
+		// grant origin's user list clone permission to fork
+		List<String> users = repositoryManager.getRepositoryUsers(repository);
+		List<UserModel> cloneUsers = new ArrayList<UserModel>();
+		for (String name : users) {
+			if (!name.equalsIgnoreCase(user.username)) {
+				UserModel cloneUser = userManager.getUserModel(name);
+				if (cloneUser.canClone(repository)) {
+					// origin user can clone origin, grant clone access to fork
+					cloneUser.setRepositoryPermission(cloneName, AccessPermission.CLONE);
+				}
+				cloneUsers.add(cloneUser);
+			}
+		}
+		userManager.updateUserModels(cloneUsers);
+
+		// grant origin's team list clone permission to fork
+		List<String> teams = repositoryManager.getRepositoryTeams(repository);
+		List<TeamModel> cloneTeams = new ArrayList<TeamModel>();
+		for (String name : teams) {
+			TeamModel cloneTeam = userManager.getTeamModel(name);
+			if (cloneTeam.canClone(repository)) {
+				// origin team can clone origin, grant clone access to fork
+				cloneTeam.setRepositoryPermission(cloneName, AccessPermission.CLONE);
+			}
+			cloneTeams.add(cloneTeam);
+		}
+		userManager.updateTeamModels(cloneTeams);
+
+		// add this clone to the cached model
+		repositoryManager.addToCachedRepositoryList(cloneModel);
+		return cloneModel;
+	}
+
+	/**
+	 * Adds a TeamModel object.
+	 *
+	 * @param team
+	 */
+	@Override
+	public void addTeam(TeamModel team) throws GitBlitException {
+		if (!userManager.updateTeamModel(team)) {
+			throw new GitBlitException("Failed to add team!");
+		}
+	}
+
+	/**
+	 * Updates the TeamModel object for the specified name.
+	 *
+	 * @param teamname
+	 * @param team
+	 */
+	@Override
+	public void reviseTeam(String teamname, TeamModel team) throws GitBlitException {
+		if (!teamname.equalsIgnoreCase(team.name)) {
+			if (userManager.getTeamModel(team.name) != null) {
+				throw new GitBlitException(MessageFormat.format(
+						"Failed to rename ''{0}'' because ''{1}'' already exists.", teamname,
+						team.name));
+			}
+		}
+		if (!userManager.updateTeamModel(teamname, team)) {
+			throw new GitBlitException("Failed to update team!");
+		}
+	}
+
+	/**
+	 * Adds a user object.
+	 *
+	 * @param user
+	 * @throws GitBlitException
+	 */
+	@Override
+	public void addUser(UserModel user) throws GitBlitException {
+		if (!userManager.updateUserModel(user)) {
+			throw new GitBlitException("Failed to add user!");
+		}
+	}
+
+	/**
+	 * Updates a user object keyed by username. This method allows
+	 * for renaming a user.
+	 *
+	 * @param username
+	 * @param user
+	 * @throws GitBlitException
+	 */
+	@Override
+	public void reviseUser(String username, UserModel user) throws GitBlitException {
+		if (!username.equalsIgnoreCase(user.username)) {
+			if (userManager.getUserModel(user.username) != null) {
+				throw new GitBlitException(MessageFormat.format(
+						"Failed to rename ''{0}'' because ''{1}'' already exists.", username,
+						user.username));
+			}
+
+			// rename repositories and owner fields for all repositories
+			for (RepositoryModel model : repositoryManager.getRepositoryModels(user)) {
+				if (model.isUsersPersonalRepository(username)) {
+					// personal repository
+					model.addOwner(user.username);
+					String oldRepositoryName = model.name;
+					model.name = user.getPersonalPath() + model.name.substring(model.projectPath.length());
+					model.projectPath = user.getPersonalPath();
+					repositoryManager.updateRepositoryModel(oldRepositoryName, model, false);
+				} else if (model.isOwner(username)) {
+					// common/shared repo
+					model.addOwner(user.username);
+					repositoryManager.updateRepositoryModel(model.name, model, false);
+				}
+			}
+		}
+		if (!userManager.updateUserModel(username, user)) {
+			throw new GitBlitException("Failed to update user!");
+		}
+	}
+
+	/**
+	 * Returns a list of repository URLs and the user access permission.
+	 *
+	 * @param request
+	 * @param user
+	 * @param repository
+	 * @return a list of repository urls
+	 */
+	@Override
+	public List<RepositoryUrl> getRepositoryUrls(HttpServletRequest request, UserModel user, RepositoryModel repository) {
+		if (user == null) {
+			user = UserModel.ANONYMOUS;
+		}
+		String username = StringUtils.encodeUsername(UserModel.ANONYMOUS.equals(user) ? "" : user.username);
+
+		List<RepositoryUrl> list = new ArrayList<RepositoryUrl>();
+		// http/https url
+		if (settings.getBoolean(Keys.git.enableGitServlet, true)) {
+			AccessPermission permission = user.getRepositoryPermission(repository).permission;
+			if (permission.exceeds(AccessPermission.NONE)) {
+				list.add(new RepositoryUrl(getRepositoryUrl(request, username, repository), permission));
+			}
+		}
+
+		// add all other urls
+		// {0} = repository
+		// {1} = username
+		for (String url : settings.getStrings(Keys.web.otherUrls)) {
+			if (url.contains("{1}")) {
+				// external url requires username, only add url IF we have one
+				if (!StringUtils.isEmpty(username)) {
+					list.add(new RepositoryUrl(MessageFormat.format(url, repository.name, username), null));
+				}
+			} else {
+				// external url does not require username
+				list.add(new RepositoryUrl(MessageFormat.format(url, repository.name), null));
+			}
+		}
+		return list;
+	}
+
+	protected String getRepositoryUrl(HttpServletRequest request, String username, RepositoryModel repository) {
+		StringBuilder sb = new StringBuilder();
+		sb.append(HttpUtils.getGitblitURL(request));
+		sb.append(Constants.R_PATH);
+		sb.append(repository.name);
+
+		// inject username into repository url if authentication is required
+		if (repository.accessRestriction.exceeds(AccessRestrictionType.NONE)
+				&& !StringUtils.isEmpty(username)) {
+			sb.insert(sb.indexOf("://") + 3, username + "@");
+		}
+		return sb.toString();
+	}
+
+
+	/**
+	 * Returns the list of custom client applications to be used for the
+	 * repository url panel;
+	 *
+	 * @return a collection of client applications
+	 */
+	@Override
+	public Collection<GitClientApplication> getClientApplications() {
+		// prefer user definitions, if they exist
+		File userDefs = new File(runtimeManager.getBaseFolder(), "clientapps.json");
+		if (userDefs.exists()) {
+			Date lastModified = new Date(userDefs.lastModified());
+			if (clientApplications.hasCurrent("user", lastModified)) {
+				return clientApplications.getObject("user");
+			} else {
+				// (re)load user definitions
+				try {
+					InputStream is = new FileInputStream(userDefs);
+					Collection<GitClientApplication> clients = readClientApplications(is);
+					is.close();
+					if (clients != null) {
+						clientApplications.updateObject("user", lastModified, clients);
+						return clients;
+					}
+				} catch (IOException e) {
+					logger.error("Failed to deserialize " + userDefs.getAbsolutePath(), e);
+				}
+			}
+		}
+
+		// no user definitions, use system definitions
+		if (!clientApplications.hasCurrent("system", new Date(0))) {
+			try {
+				InputStream is = getClass().getResourceAsStream("/clientapps.json");
+				Collection<GitClientApplication> clients = readClientApplications(is);
+				is.close();
+				if (clients != null) {
+					clientApplications.updateObject("system", new Date(0), clients);
+				}
+			} catch (IOException e) {
+				logger.error("Failed to deserialize clientapps.json resource!", e);
+			}
+		}
+
+		return clientApplications.getObject("system");
+	}
+
+	private Collection<GitClientApplication> readClientApplications(InputStream is) {
+		try {
+			Type type = new TypeToken<Collection<GitClientApplication>>() {
+			}.getType();
+			InputStreamReader reader = new InputStreamReader(is);
+			Gson gson = JsonUtils.gson();
+			Collection<GitClientApplication> links = gson.fromJson(reader, type);
+			return links;
+		} catch (JsonIOException e) {
+			logger.error("Error deserializing client applications!", e);
+		} catch (JsonSyntaxException e) {
+			logger.error("Error deserializing client applications!", e);
+		}
+		return null;
+	}
+
+	/**
+	 * Parse the properties file and aggregate all the comments by the setting
+	 * key. A setting model tracks the current value, the default value, the
+	 * description of the setting and and directives about the setting.
+	 *
+	 * @return Map<String, SettingModel>
+	 */
+	private void loadSettingModels(ServerSettings settingsModel) {
+		try {
+			// Read bundled Gitblit properties to extract setting descriptions.
+			// This copy is pristine and only used for populating the setting
+			// models map.
+			InputStream is = getClass().getResourceAsStream("/reference.properties");
+			BufferedReader propertiesReader = new BufferedReader(new InputStreamReader(is));
+			StringBuilder description = new StringBuilder();
+			SettingModel setting = new SettingModel();
+			String line = null;
+			while ((line = propertiesReader.readLine()) != null) {
+				if (line.length() == 0) {
+					description.setLength(0);
+					setting = new SettingModel();
+				} else {
+					if (line.charAt(0) == '#') {
+						if (line.length() > 1) {
+							String text = line.substring(1).trim();
+							if (SettingModel.CASE_SENSITIVE.equals(text)) {
+								setting.caseSensitive = true;
+							} else if (SettingModel.RESTART_REQUIRED.equals(text)) {
+								setting.restartRequired = true;
+							} else if (SettingModel.SPACE_DELIMITED.equals(text)) {
+								setting.spaceDelimited = true;
+							} else if (text.startsWith(SettingModel.SINCE)) {
+								try {
+									setting.since = text.split(" ")[1];
+								} catch (Exception e) {
+									setting.since = text;
+								}
+							} else {
+								description.append(text);
+								description.append('\n');
+							}
+						}
+					} else {
+						String[] kvp = line.split("=", 2);
+						String key = kvp[0].trim();
+						setting.name = key;
+						setting.defaultValue = kvp[1].trim();
+						setting.currentValue = setting.defaultValue;
+						setting.description = description.toString().trim();
+						settingsModel.add(setting);
+						description.setLength(0);
+						setting = new SettingModel();
+					}
+				}
+			}
+			propertiesReader.close();
+		} catch (NullPointerException e) {
+			logger.error("Failed to find resource copy of gitblit.properties");
+		} catch (IOException e) {
+			logger.error("Failed to load resource copy of gitblit.properties");
+		}
+	}
+
+	/*
+	 * ISTOREDSETTINGS
+	 *
+	 * these methods are necessary for (nearly) seamless Groovy hook operation
+	 * after the massive refactor.
+	 */
+
+	public boolean getBoolean(String key, boolean defaultValue) {
+		return runtimeManager.getSettings().getBoolean(key, defaultValue);
+	}
+
+	public String getString(String key, String defaultValue) {
+		return runtimeManager.getSettings().getString(key, defaultValue);
+	}
+
+	public int getInteger(String key, int defaultValue) {
+		return runtimeManager.getSettings().getInteger(key, defaultValue);
+	}
+
+	public List<String> getStrings(String key) {
+		return runtimeManager.getSettings().getStrings(key);
+	}
+
+	/*
+	 * RUNTIME MANAGER
+	 */
+
+	@Override
+	public File getBaseFolder() {
+		return runtimeManager.getBaseFolder();
+	}
+
+	@Override
+	public void setBaseFolder(File folder) {
+		runtimeManager.setBaseFolder(folder);
+	}
+
+	@Override
+	public Date getBootDate() {
+		return runtimeManager.getBootDate();
+	}
+
+	@Override
+	public ServerSettings getSettingsModel() {
+		return runtimeManager.getSettingsModel();
+	}
+
+	@Override
+	public boolean isServingRepositories() {
+		return runtimeManager.isServingRepositories();
+	}
+
+	@Override
+	public TimeZone getTimezone() {
+		return runtimeManager.getTimezone();
+	}
+
+	@Override
+	public boolean isDebugMode() {
+		return runtimeManager.isDebugMode();
+	}
+
+	@Override
+	public File getFileOrFolder(String key, String defaultFileOrFolder) {
+		return runtimeManager.getFileOrFolder(key, defaultFileOrFolder);
+	}
+
+	@Override
+	public File getFileOrFolder(String fileOrFolder) {
+		return runtimeManager.getFileOrFolder(fileOrFolder);
+	}
+
+	@Override
+	public IStoredSettings getSettings() {
+		return runtimeManager.getSettings();
+	}
+
+	@Override
+	public boolean updateSettings(Map<String, String> updatedSettings) {
+		return runtimeManager.updateSettings(updatedSettings);
+	}
+
+	@Override
+	public ServerStatus getStatus() {
+		return runtimeManager.getStatus();
+	}
+
+	/*
+	 * NOTIFICATION MANAGER
+	 */
+
+	@Override
+	public void sendMailToAdministrators(String subject, String message) {
+		notificationManager.sendMailToAdministrators(subject, message);
+	}
+
+	@Override
+	public void sendMail(String subject, String message, Collection<String> toAddresses) {
+		notificationManager.sendMail(subject, message, toAddresses);
+	}
+
+	@Override
+	public void sendMail(String subject, String message, String... toAddresses) {
+		notificationManager.sendMail(subject, message, toAddresses);
+	}
+
+	@Override
+	public void sendHtmlMail(String subject, String message, Collection<String> toAddresses) {
+		notificationManager.sendHtmlMail(subject, message, toAddresses);
+	}
+
+	@Override
+	public void sendHtmlMail(String subject, String message, String... toAddresses) {
+		notificationManager.sendHtmlMail(subject, message, toAddresses);
+	}
+
+	/*
+	 * SESSION MANAGER
+	 */
+
+	@Override
+	public UserModel authenticate(String username, char[] password) {
+		return authenticationManager.authenticate(username, password);
+	}
+
+	@Override
+	public UserModel authenticate(HttpServletRequest httpRequest) {
+		UserModel user = authenticationManager.authenticate(httpRequest, false);
+		if (user == null) {
+			user = federationManager.authenticate(httpRequest);
+		}
+		return user;
+	}
+	@Override
+	public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) {
+		UserModel user = authenticationManager.authenticate(httpRequest, requiresCertificate);
+		if (user == null) {
+			user = federationManager.authenticate(httpRequest);
+		}
+		return user;
+	}
+
+	@Override
+	public void setCookie(HttpServletResponse response, UserModel user) {
+		authenticationManager.setCookie(response, user);
+	}
+
+	@Override
+	public void logout(HttpServletResponse response, UserModel user) {
+		authenticationManager.logout(response, user);
+	}
+
+	@Override
+	public boolean supportsCredentialChanges(UserModel user) {
+		return authenticationManager.supportsCredentialChanges(user);
+	}
+
+	@Override
+	public boolean supportsDisplayNameChanges(UserModel user) {
+		return authenticationManager.supportsDisplayNameChanges(user);
+	}
+
+	@Override
+	public boolean supportsEmailAddressChanges(UserModel user) {
+		return authenticationManager.supportsEmailAddressChanges(user);
+	}
+
+	@Override
+	public boolean supportsTeamMembershipChanges(UserModel user) {
+		return authenticationManager.supportsTeamMembershipChanges(user);
+	}
+
+	@Override
+	public boolean supportsTeamMembershipChanges(TeamModel team) {
+		return authenticationManager.supportsTeamMembershipChanges(team);
+	}
+
+	/*
+	 * USER MANAGER
+	 */
+
+	@Override
+	public void setup(IRuntimeManager runtimeManager) {
+	}
+
+	@Override
+	public boolean isInternalAccount(String username) {
+		return userManager.isInternalAccount(username);
+	}
+
+	@Override
+	public List<String> getAllUsernames() {
+		return userManager.getAllUsernames();
+	}
+
+	@Override
+	public List<UserModel> getAllUsers() {
+		return userManager.getAllUsers();
+	}
+
+	@Override
+	public boolean deleteUser(String username) {
+		return userManager.deleteUser(username);
+	}
+
+	@Override
+	public UserModel getUserModel(String username) {
+		return userManager.getUserModel(username);
+	}
+
+	@Override
+	public List<TeamModel> getAllTeams() {
+		return userManager.getAllTeams();
+	}
+
+	@Override
+	public TeamModel getTeamModel(String teamname) {
+		return userManager.getTeamModel(teamname);
+	}
+
+	@Override
+	public String getCookie(UserModel model) {
+		return userManager.getCookie(model);
+	}
+
+	@Override
+	public UserModel getUserModel(char[] cookie) {
+		return userManager.getUserModel(cookie);
+	}
+
+	@Override
+	public boolean updateUserModel(UserModel model) {
+		return userManager.updateUserModel(model);
+	}
+
+	@Override
+	public boolean updateUserModels(Collection<UserModel> models) {
+		return userManager.updateUserModels(models);
+	}
+
+	@Override
+	public boolean updateUserModel(String username, UserModel model) {
+		return userManager.updateUserModel(username, model);
+	}
+
+	@Override
+	public boolean deleteUserModel(UserModel model) {
+		return userManager.deleteUserModel(model);
+	}
+
+	@Override
+	public List<String> getAllTeamNames() {
+		return userManager.getAllTeamNames();
+	}
+
+	@Override
+	public List<String> getTeamNamesForRepositoryRole(String role) {
+		return userManager.getTeamNamesForRepositoryRole(role);
+	}
+
+	@Override
+	public boolean updateTeamModel(TeamModel model) {
+		return userManager.updateTeamModel(model);
+	}
+
+	@Override
+	public boolean updateTeamModels(Collection<TeamModel> models) {
+		return userManager.updateTeamModels(models);
+	}
+
+	@Override
+	public boolean updateTeamModel(String teamname, TeamModel model) {
+		return userManager.updateTeamModel(teamname, model);
+	}
+
+	@Override
+	public boolean deleteTeamModel(TeamModel model) {
+		return userManager.deleteTeamModel(model);
+	}
+
+	@Override
+	public List<String> getUsernamesForRepositoryRole(String role) {
+		return userManager.getUsernamesForRepositoryRole(role);
+	}
+
+	@Override
+	public boolean renameRepositoryRole(String oldRole, String newRole) {
+		return userManager.renameRepositoryRole(oldRole, newRole);
+	}
+
+	@Override
+	public boolean deleteRepositoryRole(String role) {
+		return userManager.deleteRepositoryRole(role);
+	}
+
+	@Override
+	public boolean deleteTeam(String teamname) {
+		return userManager.deleteTeam(teamname);
+	}
+
+	/*
+	 * REPOSITORY MANAGER
+	 */
+
+	@Override
+	public Date getLastActivityDate() {
+		return repositoryManager.getLastActivityDate();
+	}
+
+	@Override
+	public File getRepositoriesFolder() {
+		return repositoryManager.getRepositoriesFolder();
+	}
+
+	@Override
+	public File getHooksFolder() {
+		return repositoryManager.getHooksFolder();
+	}
+
+	@Override
+	public File getGrapesFolder() {
+		return repositoryManager.getGrapesFolder();
+	}
+
+	@Override
+	public List<RegistrantAccessPermission> getUserAccessPermissions(UserModel user) {
+		return repositoryManager.getUserAccessPermissions(user);
+	}
+
+	@Override
+	public List<RegistrantAccessPermission> getUserAccessPermissions(RepositoryModel repository) {
+		return repositoryManager.getUserAccessPermissions(repository);
+	}
+
+	@Override
+	public boolean setUserAccessPermissions(RepositoryModel repository, Collection<RegistrantAccessPermission> permissions) {
+		return repositoryManager.setUserAccessPermissions(repository, permissions);
+	}
+
+	@Override
+	public List<String> getRepositoryUsers(RepositoryModel repository) {
+		return repositoryManager.getRepositoryUsers(repository);
+	}
+
+	@Override
+	public List<RegistrantAccessPermission> getTeamAccessPermissions(RepositoryModel repository) {
+		return repositoryManager.getTeamAccessPermissions(repository);
+	}
+
+	@Override
+	public boolean setTeamAccessPermissions(RepositoryModel repository, Collection<RegistrantAccessPermission> permissions) {
+		return repositoryManager.setTeamAccessPermissions(repository, permissions);
+	}
+
+	@Override
+	public List<String> getRepositoryTeams(RepositoryModel repository) {
+		return repositoryManager.getRepositoryTeams(repository);
+	}
+	@Override
+	public void addToCachedRepositoryList(RepositoryModel model) {
+		repositoryManager.addToCachedRepositoryList(model);
+	}
+
+	@Override
+	public void resetRepositoryListCache() {
+		repositoryManager.resetRepositoryListCache();
+	}
+
+	@Override
+	public List<String> getRepositoryList() {
+		return repositoryManager.getRepositoryList();
+	}
+
+	@Override
+	public Repository getRepository(String repositoryName) {
+		return repositoryManager.getRepository(repositoryName);
+	}
+
+	@Override
+	public Repository getRepository(String repositoryName, boolean logError) {
+		return repositoryManager.getRepository(repositoryName, logError);
+	}
+
+	@Override
+	public List<RepositoryModel> getRepositoryModels(UserModel user) {
+		return repositoryManager.getRepositoryModels(user);
+	}
+
+	@Override
+	public RepositoryModel getRepositoryModel(UserModel user, String repositoryName) {
+		return repositoryManager.getRepositoryModel(repositoryName);
+	}
+
+	@Override
+	public RepositoryModel getRepositoryModel(String repositoryName) {
+		return repositoryManager.getRepositoryModel(repositoryName);
+	}
+
+	@Override
+	public long getStarCount(RepositoryModel repository) {
+		return repositoryManager.getStarCount(repository);
+	}
+
+	@Override
+	public boolean hasRepository(String repositoryName) {
+		return repositoryManager.hasRepository(repositoryName);
+	}
+
+	@Override
+	public boolean hasRepository(String repositoryName, boolean caseSensitiveCheck) {
+		return repositoryManager.hasRepository(repositoryName, caseSensitiveCheck);
+	}
+
+	@Override
+	public boolean hasFork(String username, String origin) {
+		return repositoryManager.hasFork(username, origin);
+	}
+
+	@Override
+	public String getFork(String username, String origin) {
+		return repositoryManager.getFork(username, origin);
+	}
+
+	@Override
+	public ForkModel getForkNetwork(String repository) {
+		return repositoryManager.getForkNetwork(repository);
+	}
+
+	@Override
+	public long updateLastChangeFields(Repository r, RepositoryModel model) {
+		return repositoryManager.updateLastChangeFields(r, model);
+	}
+
+	@Override
+	public List<Metric> getRepositoryDefaultMetrics(RepositoryModel model, Repository repository) {
+		return repositoryManager.getRepositoryDefaultMetrics(model, repository);
+	}
+
+	@Override
+	public void updateRepositoryModel(String repositoryName, RepositoryModel repository,
+			boolean isCreate) throws GitBlitException {
+		repositoryManager.updateRepositoryModel(repositoryName, repository, isCreate);
+	}
+
+	@Override
+	public void updateConfiguration(Repository r, RepositoryModel repository) {
+		repositoryManager.updateConfiguration(r, repository);
+	}
+
+	@Override
+	public boolean deleteRepositoryModel(RepositoryModel model) {
+		return repositoryManager.deleteRepositoryModel(model);
+	}
+
+	@Override
+	public boolean deleteRepository(String repositoryName) {
+		return repositoryManager.deleteRepository(repositoryName);
+	}
+
+	@Override
+	public List<String> getAllScripts() {
+		return repositoryManager.getAllScripts();
+	}
+
+	@Override
+	public List<String> getPreReceiveScriptsInherited(RepositoryModel repository) {
+		return repositoryManager.getPreReceiveScriptsInherited(repository);
+	}
+
+	@Override
+	public List<String> getPreReceiveScriptsUnused(RepositoryModel repository) {
+		return repositoryManager.getPreReceiveScriptsUnused(repository);
+	}
+
+	@Override
+	public List<String> getPostReceiveScriptsInherited(RepositoryModel repository) {
+		return repositoryManager.getPostReceiveScriptsInherited(repository);
+	}
+
+	@Override
+	public List<String> getPostReceiveScriptsUnused(RepositoryModel repository) {
+		return repositoryManager.getPostReceiveScriptsUnused(repository);
+	}
+
+	@Override
+	public List<SearchResult> search(String query, int page, int pageSize, List<String> repositories) {
+		return repositoryManager.search(query, page, pageSize, repositories);
+	}
+
+	@Override
+	public boolean isCollectingGarbage() {
+		return repositoryManager.isCollectingGarbage();
+	}
+
+	@Override
+	public boolean isCollectingGarbage(String repositoryName) {
+		return repositoryManager.isCollectingGarbage(repositoryName);
+	}
+
+	/*
+	 * PROJECT MANAGER
+	 */
+
+	@Override
+	public List<ProjectModel> getProjectModels(UserModel user, boolean includeUsers) {
+		return projectManager.getProjectModels(user, includeUsers);
+	}
+
+	@Override
+	public ProjectModel getProjectModel(String name, UserModel user) {
+		return projectManager.getProjectModel(name, user);
+	}
+
+	@Override
+	public ProjectModel getProjectModel(String name) {
+		return projectManager.getProjectModel(name);
+	}
+
+	@Override
+	public List<ProjectModel> getProjectModels(List<RepositoryModel> repositoryModels, boolean includeUsers) {
+		return projectManager.getProjectModels(repositoryModels, includeUsers);
+	}
+
+	/*
+	 * FEDERATION MANAGER
+	 */
+
+	@Override
+	public File getProposalsFolder() {
+		return federationManager.getProposalsFolder();
+	}
+
+	@Override
+	public boolean canFederate() {
+		return federationManager.canFederate();
+	}
+
+	@Override
+	public UserModel getFederationUser() {
+		return federationManager.getFederationUser();
+	}
+
+	@Override
+	public List<FederationModel> getFederationRegistrations() {
+		return federationManager.getFederationRegistrations();
+	}
+
+	@Override
+	public FederationModel getFederationRegistration(String url, String name) {
+		return federationManager.getFederationRegistration(url, name);
+	}
+
+	@Override
+	public List<FederationSet> getFederationSets(String gitblitUrl) {
+		return federationManager.getFederationSets(gitblitUrl);
+	}
+
+	@Override
+	public List<String> getFederationTokens() {
+		return federationManager.getFederationTokens();
+	}
+
+	@Override
+	public String getFederationToken(FederationToken type) {
+		return federationManager.getFederationToken(type);
+	}
+
+	@Override
+	public String getFederationToken(String value) {
+		return federationManager.getFederationToken(value);
+	}
+
+	@Override
+	public boolean validateFederationRequest(FederationRequest req, String token) {
+		return federationManager.validateFederationRequest(req, token);
+	}
+
+	@Override
+	public boolean acknowledgeFederationStatus(String identification, FederationModel registration) {
+		return federationManager.acknowledgeFederationStatus(identification, registration);
+	}
+
+	@Override
+	public List<FederationModel> getFederationResultRegistrations() {
+		return federationManager.getFederationResultRegistrations();
+	}
+
+	@Override
+	public boolean submitFederationProposal(FederationProposal proposal, String gitblitUrl) {
+		return federationManager.submitFederationProposal(proposal, gitblitUrl);
+	}
+
+	@Override
+	public List<FederationProposal> getPendingFederationProposals() {
+		return federationManager.getPendingFederationProposals();
+	}
+
+	@Override
+	public Map<String, RepositoryModel> getRepositories(String gitblitUrl, String token) {
+		return federationManager.getRepositories(gitblitUrl, token);
+	}
+
+	@Override
+	public FederationProposal createFederationProposal(String gitblitUrl, String token) {
+		return federationManager.createFederationProposal(gitblitUrl, token);
+	}
+
+	@Override
+	public FederationProposal getPendingFederationProposal(String token) {
+		return federationManager.getPendingFederationProposal(token);
+	}
+
+	@Override
+	public boolean deletePendingFederationProposal(FederationProposal proposal) {
+		return federationManager.deletePendingFederationProposal(proposal);
+	}
+}
diff --git a/src/main/java/com/gitblit/manager/IFederationManager.java b/src/main/java/com/gitblit/manager/IFederationManager.java
index 5afdeea..d5880c0 100644
--- a/src/main/java/com/gitblit/manager/IFederationManager.java
+++ b/src/main/java/com/gitblit/manager/IFederationManager.java
@@ -19,6 +19,8 @@
 import java.util.List;
 import java.util.Map;
 
+import javax.servlet.http.HttpServletRequest;
+
 import com.gitblit.Constants.FederationRequest;
 import com.gitblit.Constants.FederationToken;
 import com.gitblit.models.FederationModel;
@@ -37,9 +39,22 @@
 	 */
 	File getProposalsFolder();
 
+	boolean canFederate();
+
+	/**
+	 * Returns the federation user account.
+	 *
+	 * @return the federation user account
+	 */
 	UserModel getFederationUser();
 
-	boolean canFederate();
+	/**
+	 * Try to authenticate request as the Federation user.
+	 *
+	 * @param httpRequest
+	 * @return the federation user, if authenticated
+	 */
+	UserModel authenticate(HttpServletRequest httpRequest);
 
 	/**
 	 * Returns the list of federated gitblit instances that this instance will
diff --git a/src/main/java/com/gitblit/manager/IUserManager.java b/src/main/java/com/gitblit/manager/IUserManager.java
index 5815bca..945d6a8 100644
--- a/src/main/java/com/gitblit/manager/IUserManager.java
+++ b/src/main/java/com/gitblit/manager/IUserManager.java
@@ -19,5 +19,12 @@
 
 public interface IUserManager extends IManager, IUserService {
 
+	/**
+	 * Returns true if the username represents an internal account
+	 *
+	 * @param username
+	 * @return true if the specified username represents an internal account
+	 */
+	boolean isInternalAccount(String username);
 
 }
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/manager/ServicesManager.java b/src/main/java/com/gitblit/manager/ServicesManager.java
index 6cc9456..8107a7d 100644
--- a/src/main/java/com/gitblit/manager/ServicesManager.java
+++ b/src/main/java/com/gitblit/manager/ServicesManager.java
@@ -32,7 +32,6 @@
 import com.gitblit.Constants.AccessPermission;
 import com.gitblit.Constants.AccessRestrictionType;
 import com.gitblit.Constants.FederationToken;
-import com.gitblit.GitBlit;
 import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
 import com.gitblit.fanout.FanoutNioService;
@@ -62,13 +61,13 @@
 
 	private final IStoredSettings settings;
 
-	private final GitBlit gitblit;
+	private final IGitblit gitblit;
 
 	private FanoutService fanoutService;
 
 	private GitDaemon gitDaemon;
 
-	public ServicesManager(GitBlit gitblit) {
+	public ServicesManager(IGitblit gitblit) {
 		this.settings = gitblit.getSettings();
 		this.gitblit = gitblit;
 	}
@@ -209,11 +208,11 @@
 	private class FederationPuller extends FederationPullService {
 
 		public FederationPuller(FederationModel registration) {
-			super(Arrays.asList(registration));
+			super(gitblit, Arrays.asList(registration));
 		}
 
 		public FederationPuller(List<FederationModel> registrations) {
-			super(registrations);
+			super(gitblit, registrations);
 		}
 
 		@Override
diff --git a/src/main/java/com/gitblit/manager/UserManager.java b/src/main/java/com/gitblit/manager/UserManager.java
index 3ca62e2..19943d7 100644
--- a/src/main/java/com/gitblit/manager/UserManager.java
+++ b/src/main/java/com/gitblit/manager/UserManager.java
@@ -28,6 +28,7 @@
 import org.slf4j.LoggerFactory;
 
 import com.gitblit.ConfigUserService;
+import com.gitblit.Constants;
 import com.gitblit.IStoredSettings;
 import com.gitblit.IUserService;
 import com.gitblit.Keys;
@@ -149,6 +150,19 @@
 	}
 
 	/**
+	 * Returns true if the username represents an internal account
+	 *
+	 * @param username
+	 * @return true if the specified username represents an internal account
+	 */
+	@Override
+	public boolean isInternalAccount(String username) {
+		return !StringUtils.isEmpty(username)
+				&& (username.equalsIgnoreCase(Constants.FEDERATION_USER)
+						|| username.equalsIgnoreCase(UserModel.ANONYMOUS.username));
+	}
+
+	/**
 	 * Returns the cookie value for the specified user.
 	 *
 	 * @param model
diff --git a/src/main/java/com/gitblit/service/FederationPullService.java b/src/main/java/com/gitblit/service/FederationPullService.java
index e2a50f2..629cbf9 100644
--- a/src/main/java/com/gitblit/service/FederationPullService.java
+++ b/src/main/java/com/gitblit/service/FederationPullService.java
@@ -31,10 +31,10 @@
 import com.gitblit.Constants.AccessPermission;
 import com.gitblit.Constants.FederationPullStatus;
 import com.gitblit.Constants.FederationStrategy;
-import com.gitblit.GitBlit;
 import com.gitblit.GitBlitException.ForbiddenException;
 import com.gitblit.IUserService;
 import com.gitblit.Keys;
+import com.gitblit.manager.IGitblit;
 import com.gitblit.models.FederationModel;
 import com.gitblit.models.RefModel;
 import com.gitblit.models.RepositoryModel;
@@ -49,9 +49,9 @@
 
 public abstract class FederationPullService implements Runnable {
 
-	Logger logger = LoggerFactory.getLogger(getClass());
+	final Logger logger = LoggerFactory.getLogger(getClass());
 
-	GitBlit gitblit;
+	final IGitblit gitblit;
 
 	private final List<FederationModel> registrations;
 
@@ -62,8 +62,8 @@
 	 * @param provider
 	 * @param registration
 	 */
-	public FederationPullService(FederationModel registration) {
-		this(Arrays.asList(registration));
+	public FederationPullService(IGitblit gitblit, FederationModel registration) {
+		this(gitblit, Arrays.asList(registration));
 	}
 
 	/**
@@ -77,7 +77,8 @@
 	 *            if true, registrations are rescheduled in perpetuity. if
 	 *            false, the federation pull operation is executed once.
 	 */
-	public FederationPullService(List<FederationModel> registrations) {
+	public FederationPullService(IGitblit gitblit, List<FederationModel> registrations) {
+		this.gitblit = gitblit;
 		this.registrations = registrations;
 	}
 
diff --git a/src/main/java/com/gitblit/servlet/FederationServlet.java b/src/main/java/com/gitblit/servlet/FederationServlet.java
index e86e5d6..4d3cbbe 100644
--- a/src/main/java/com/gitblit/servlet/FederationServlet.java
+++ b/src/main/java/com/gitblit/servlet/FederationServlet.java
@@ -29,13 +29,9 @@
 import javax.inject.Singleton;
 import javax.servlet.http.HttpServletResponse;
 
-import com.gitblit.Constants;
+import com.gitblit.Constants.FederationRequest;
 import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
-import com.gitblit.Constants.FederationRequest;
-import com.gitblit.Keys.federation;
-import com.gitblit.Keys.git;
-import com.gitblit.Keys.groovy;
 import com.gitblit.manager.IFederationManager;
 import com.gitblit.manager.IRepositoryManager;
 import com.gitblit.manager.IRuntimeManager;
diff --git a/src/main/java/com/gitblit/servlet/GitFilter.java b/src/main/java/com/gitblit/servlet/GitFilter.java
index 402636e..15facbe 100644
--- a/src/main/java/com/gitblit/servlet/GitFilter.java
+++ b/src/main/java/com/gitblit/servlet/GitFilter.java
@@ -18,6 +18,7 @@
 import java.text.MessageFormat;
 
 import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
 
 import com.gitblit.Constants.AccessRestrictionType;
 import com.gitblit.Constants.AuthorizationControl;
@@ -25,8 +26,10 @@
 import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
 import com.gitblit.manager.IAuthenticationManager;
+import com.gitblit.manager.IFederationManager;
 import com.gitblit.manager.IRepositoryManager;
 import com.gitblit.manager.IRuntimeManager;
+import com.gitblit.manager.IUserManager;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.UserModel;
 import com.gitblit.utils.StringUtils;
@@ -50,14 +53,22 @@
 
 	private final IStoredSettings settings;
 
+	private final IUserManager userManager;
+
+	private final IFederationManager federationManager;
+
 	@Inject
 	public GitFilter(
 			IRuntimeManager runtimeManager,
+			IUserManager userManager,
 			IAuthenticationManager authenticationManager,
-			IRepositoryManager repositoryManager) {
+			IRepositoryManager repositoryManager,
+			IFederationManager federationManager) {
 
 		super(runtimeManager, authenticationManager, repositoryManager);
 		this.settings = runtimeManager.getSettings();
+		this.userManager = userManager;
+		this.federationManager = federationManager;
 	}
 
 	/**
@@ -114,6 +125,21 @@
 	}
 
 	/**
+	 * Returns the user making the request, if the user has authenticated.
+	 *
+	 * @param httpRequest
+	 * @return user
+	 */
+	@Override
+	protected UserModel getUser(HttpServletRequest httpRequest) {
+		UserModel user = authenticationManager.authenticate(httpRequest, requiresClientCertificate());
+		if (user == null) {
+			user = federationManager.authenticate(httpRequest);
+		}
+		return user;
+	}
+
+	/**
 	 * Determine if a non-existing repository can be created using this filter.
 	 *
 	 * @return true if the server allows repository creation on-push

--
Gitblit v1.9.1