src/main/java/com/gitblit/DaggerModule.java
@@ -20,18 +20,22 @@ import org.apache.wicket.protocol.http.WebApplication; import com.gitblit.git.GitServlet; import com.gitblit.manager.FederationManager; import com.gitblit.manager.GitblitManager; import com.gitblit.manager.IFederationManager; import com.gitblit.manager.IGitblitManager; import com.gitblit.manager.INotificationManager; import com.gitblit.manager.IProjectManager; import com.gitblit.manager.IRepositoryManager; import com.gitblit.manager.IRuntimeManager; import com.gitblit.manager.IServicesManager; import com.gitblit.manager.ISessionManager; import com.gitblit.manager.IUserManager; import com.gitblit.manager.NotificationManager; import com.gitblit.manager.ProjectManager; import com.gitblit.manager.RepositoryManager; import com.gitblit.manager.RuntimeManager; import com.gitblit.manager.ServicesManager; import com.gitblit.manager.SessionManager; import com.gitblit.manager.UserManager; import com.gitblit.wicket.GitBlitWebApp; @@ -47,6 +51,7 @@ * */ @Module( library = true, injects = { IStoredSettings.class, @@ -59,6 +64,7 @@ IProjectManager.class, IGitblitManager.class, IFederationManager.class, IServicesManager.class, // the monolithic manager Gitblit.class, @@ -84,13 +90,6 @@ } ) public class DaggerModule { final GitBlit gitblit; // HACK but necessary for now public DaggerModule(GitBlit gitblit) { this.gitblit = gitblit; } @Provides @Singleton IStoredSettings provideSettings() { return new FileSettings(); @@ -137,12 +136,28 @@ repositoryManager); } @Provides @Singleton IGitblitManager provideGitblitManager() { return gitblit; @Provides @Singleton IFederationManager provideFederationManager( IRuntimeManager runtimeManager, INotificationManager notificationManager, IUserManager userManager, IRepositoryManager repositoryManager) { return new FederationManager( runtimeManager, notificationManager, userManager, repositoryManager); } @Provides @Singleton IFederationManager provideFederationManager() { return gitblit; @Provides @Singleton IGitblitManager provideGitblitManager( IRuntimeManager runtimeManager, IUserManager userManager, IRepositoryManager repositoryManager) { return new GitblitManager( runtimeManager, userManager, repositoryManager); } @Provides @Singleton Gitblit provideGitblit( @@ -162,8 +177,12 @@ sessionManager, repositoryManager, projectManager, federationManager, gitblitManager); gitblitManager, federationManager); } @Provides @Singleton IServicesManager provideServicesManager(Gitblit gitblit) { return new ServicesManager(gitblit); } @Provides @Singleton WebApplication provideWebApplication( src/main/java/com/gitblit/FederationClient.java
@@ -23,6 +23,11 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; import com.beust.jcommander.Parameters; import com.gitblit.manager.FederationManager; import com.gitblit.manager.NotificationManager; import com.gitblit.manager.RepositoryManager; import com.gitblit.manager.RuntimeManager; import com.gitblit.manager.UserManager; import com.gitblit.models.FederationModel; import com.gitblit.utils.FederationUtils; import com.gitblit.utils.StringUtils; @@ -53,7 +58,7 @@ } File regFile = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, baseFolder, params.registrationsFile); IStoredSettings settings = new FileSettings(regFile.getAbsolutePath()); FileSettings settings = new FileSettings(regFile.getAbsolutePath()); List<FederationModel> registrations = new ArrayList<FederationModel>(); if (StringUtils.isEmpty(params.url)) { registrations.addAll(FederationUtils.getFederationRegistrations(settings)); @@ -83,14 +88,23 @@ } // configure the Gitblit singleton for minimal, non-server operation GitBlit gitblit = new GitBlit(settings, baseFolder); gitblit.beforeServletInjection(null); // XXX broken FederationPullExecutor executor = new FederationPullExecutor(registrations, params.isDaemon); executor.run(); if (!params.isDaemon) { RuntimeManager runtime = new RuntimeManager(settings); runtime.setBaseFolder(baseFolder); NotificationManager notifications = new NotificationManager(settings).start(); UserManager users = new UserManager(runtime).start(); RepositoryManager repositories = new RepositoryManager(runtime, users).start(); FederationManager federation = new FederationManager(runtime, notifications, users, repositories).start(); FederationPullExecutor puller = new FederationPullExecutor(federation.getFederationRegistrations()) { @Override public void reschedule(FederationModel registration) { // NOOP } }; puller.run(); System.out.println("Finished."); System.exit(0); } } private static void usage(JCommander jc, ParameterException t) { @@ -115,9 +129,6 @@ @Parameter(names = { "--registrations" }, description = "Gitblit Federation Registrations File", required = false) public String registrationsFile = "${baseFolder}/federation.properties"; @Parameter(names = { "--daemon" }, description = "Runs in daemon mode to schedule and pull repositories", required = false) public boolean isDaemon; @Parameter(names = { "--url" }, description = "URL of Gitblit instance to mirror from", required = false) public String url; src/main/java/com/gitblit/FederationPullExecutor.java
@@ -1,18 +1,3 @@ /* * Copyright 2011 gitblit.com. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gitblit; import static org.eclipse.jgit.lib.Constants.DOT_GIT_EXT; @@ -32,7 +17,6 @@ import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.TimeUnit; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; @@ -46,11 +30,6 @@ import com.gitblit.Constants.FederationPullStatus; import com.gitblit.Constants.FederationStrategy; import com.gitblit.GitBlitException.ForbiddenException; import com.gitblit.manager.IGitblitManager; import com.gitblit.manager.INotificationManager; import com.gitblit.manager.IRepositoryManager; import com.gitblit.manager.IRuntimeManager; import com.gitblit.manager.IUserManager; import com.gitblit.models.FederationModel; import com.gitblit.models.RefModel; import com.gitblit.models.RepositoryModel; @@ -62,28 +41,24 @@ import com.gitblit.utils.JGitUtils; import com.gitblit.utils.JGitUtils.CloneResult; import com.gitblit.utils.StringUtils; import com.gitblit.utils.TimeUtils; /** * FederationPullExecutor pulls repository updates and, optionally, user * accounts and server settings from registered Gitblit instances. */ public class FederationPullExecutor implements Runnable { public abstract class FederationPullExecutor implements Runnable { private final Logger logger = LoggerFactory.getLogger(FederationPullExecutor.class); Logger logger = LoggerFactory.getLogger(getClass()); Gitblit gitblit; private final List<FederationModel> registrations; private final boolean isDaemon; /** * Constructor for specifying a single federation registration. This * constructor is used to schedule the next pull execution. * * @param provider * @param registration */ private FederationPullExecutor(FederationModel registration) { this(Arrays.asList(registration), true); public FederationPullExecutor(FederationModel registration) { this(Arrays.asList(registration)); } /** @@ -91,15 +66,17 @@ * normally used at startup to pull and then schedule the next update based * on each registrations frequency setting. * * @param provider * @param registrations * @param isDaemon * if true, registrations are rescheduled in perpetuity. if * false, the federation pull operation is executed once. */ public FederationPullExecutor(List<FederationModel> registrations, boolean isDaemon) { public FederationPullExecutor(List<FederationModel> registrations) { this.registrations = registrations; this.isDaemon = isDaemon; } public abstract void reschedule(FederationModel registration); /** * Run method for this pull executor. @@ -121,9 +98,7 @@ if (registration.notifyOnError) { String message = "Federation pull of " + registration.name + " @ " + registration.url + " is now at " + is.name(); INotificationManager mailManager = GitBlit.getManager(INotificationManager.class); mailManager .sendMailToAdministrators( gitblit.sendMailToAdministrators( "Pull Status of " + registration.name + " is " + is.name(), message); } @@ -133,9 +108,7 @@ "Failed to pull from federated gitblit ({0} @ {1})", registration.name, registration.url), t); } finally { if (isDaemon) { schedule(registration); } reschedule(registration); } } } @@ -159,8 +132,7 @@ c, registrationFolder, registration.name)); return; } IRepositoryManager repositoryManager = GitBlit.getManager(IRepositoryManager.class); File repositoriesFolder = repositoryManager.getRepositoriesFolder(); File repositoriesFolder = gitblit.getRepositoriesFolder(); File registrationFolderFile = new File(repositoriesFolder, registrationFolder); registrationFolderFile.mkdirs(); @@ -200,9 +172,9 @@ // confirm that the origin of any pre-existing repository matches // the clone url String fetchHead = null; Repository existingRepository = repositoryManager.getRepository(repositoryName); Repository existingRepository = gitblit.getRepository(repositoryName); if (existingRepository == null && repositoryManager.isCollectingGarbage(repositoryName)) { if (existingRepository == null && gitblit.isCollectingGarbage(repositoryName)) { logger.warn(MessageFormat.format("Skipping local repository {0}, busy collecting garbage", repositoryName)); continue; } @@ -234,8 +206,8 @@ CloneResult result = JGitUtils.cloneRepository(registrationFolderFile, repository.name, cloneUrl, registration.bare, credentials); Repository r = repositoryManager.getRepository(repositoryName); RepositoryModel rm = repositoryManager.getRepositoryModel(repositoryName); Repository r = gitblit.getRepository(repositoryName); RepositoryModel rm = gitblit.getRepositoryModel(repositoryName); repository.isFrozen = registration.mirror; if (result.createdRepository) { // default local settings @@ -323,12 +295,10 @@ // "federated" repositories. repository.isFederated = cloneUrl.startsWith(registration.url); repositoryManager.updateConfiguration(r, repository); gitblit.updateConfiguration(r, repository); r.close(); } IUserManager userManager = GitBlit.getManager(IUserManager.class); IGitblitManager gitblitManager = GitBlit.getManager(IGitblitManager.class); IUserService userService = null; try { @@ -368,10 +338,10 @@ } // insert new user or update local user UserModel localUser = userManager.getUserModel(user.username); UserModel localUser = gitblit.getUserModel(user.username); if (localUser == null) { // create new local user gitblitManager.updateUserModel(user.username, user, true); gitblit.updateUserModel(user.username, user, true); } else { // update repository permissions of local user if (user.permissions != null) { @@ -388,19 +358,19 @@ } localUser.password = user.password; localUser.canAdmin = user.canAdmin; gitblitManager.updateUserModel(localUser.username, localUser, false); gitblit.updateUserModel(localUser.username, localUser, false); } for (String teamname : userManager.getAllTeamNames()) { TeamModel team = userManager.getTeamModel(teamname); for (String teamname : gitblit.getAllTeamNames()) { TeamModel team = gitblit.getTeamModel(teamname); if (user.isTeamMember(teamname) && !team.hasUser(user.username)) { // new team member team.addUser(user.username); userManager.updateTeamModel(teamname, team); gitblit.updateTeamModel(teamname, team); } else if (!user.isTeamMember(teamname) && team.hasUser(user.username)) { // remove team member team.removeUser(user.username); userManager.updateTeamModel(teamname, team); gitblit.updateTeamModel(teamname, team); } // update team repositories @@ -411,11 +381,11 @@ for (Map.Entry<String, AccessPermission> entry : remoteTeam.permissions.entrySet()){ team.setRepositoryPermission(entry.getKey(), entry.getValue()); } userManager.updateTeamModel(teamname, team); gitblit.updateTeamModel(teamname, team); } else if(!ArrayUtils.isEmpty(remoteTeam.repositories)) { // pulling from <= 1.1 team.addRepositoryPermissions(remoteTeam.repositories); userManager.updateTeamModel(teamname, team); gitblit.updateTeamModel(teamname, team); } } } @@ -506,28 +476,11 @@ return; } InetAddress addr = InetAddress.getLocalHost(); IStoredSettings settings = GitBlit.getManager(IRuntimeManager.class).getSettings(); String federationName = settings.getString(Keys.federation.name, null); String federationName = gitblit.getSettings().getString(Keys.federation.name, null); if (StringUtils.isEmpty(federationName)) { federationName = addr.getHostName(); } FederationUtils.acknowledgeStatus(addr.getHostAddress(), registration); logger.info(MessageFormat.format("Pull status sent to {0}", registration.url)); } /** * Schedules the next check of the federated Gitblit instance. * * @param registration */ private void schedule(FederationModel registration) { // schedule the next pull int mins = TimeUtils.convertFrequencyToMinutes(registration.frequency); registration.nextPull = new Date(System.currentTimeMillis() + (mins * 60 * 1000L)); GitBlit.self().executor() .schedule(new FederationPullExecutor(registration), mins, TimeUnit.MINUTES); logger.info(MessageFormat.format( "Next pull of {0} @ {1} scheduled for {2,date,yyyy-MM-dd HH:mm}", registration.name, registration.url, registration.nextPull)); } } src/main/java/com/gitblit/GitBlit.java
@@ -15,50 +15,25 @@ */ package com.gitblit; import java.io.BufferedReader; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.lang.reflect.Type; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.ServletContext; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpServletRequest; import org.apache.wicket.resource.ContextRelativeResource; import org.apache.wicket.util.resource.ResourceStreamNotFoundException; import org.slf4j.Logger; import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.FederationRequest; import com.gitblit.Constants.FederationToken; import com.gitblit.dagger.DaggerContextListener; import com.gitblit.fanout.FanoutNioService; import com.gitblit.fanout.FanoutService; import com.gitblit.fanout.FanoutSocketService; import com.gitblit.git.GitDaemon; import com.gitblit.git.GitServlet; import com.gitblit.manager.IFederationManager; import com.gitblit.manager.IGitblitManager; @@ -67,80 +42,54 @@ import com.gitblit.manager.IProjectManager; import com.gitblit.manager.IRepositoryManager; import com.gitblit.manager.IRuntimeManager; import com.gitblit.manager.IServicesManager; import com.gitblit.manager.ISessionManager; import com.gitblit.manager.IUserManager; import com.gitblit.models.FederationModel; import com.gitblit.models.FederationProposal; import com.gitblit.models.FederationSet; import com.gitblit.models.GitClientApplication; import com.gitblit.models.RepositoryModel; import com.gitblit.models.RepositoryUrl; import com.gitblit.models.ServerSettings; import com.gitblit.models.SettingModel; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.ContainerUtils; import com.gitblit.utils.FederationUtils; 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.gitblit.wicket.GitblitWicketFilter; import com.gitblit.wicket.WicketUtils; import com.google.gson.Gson; import com.google.gson.JsonIOException; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; import dagger.ObjectGraph; /** * GitBlit is the servlet context listener singleton that acts as the core for * the web ui and the servlets. This class is either directly instantiated by * the GitBlitServer class (Gitblit GO) or is reflectively instantiated by the * servlet 3 container (Gitblit WAR or Express). * This class is the main entry point for the entire webapp. It is a singleton * created manually by Gitblit GO or dynamically by the WAR/Express servlet * container. This class instantiates and starts all managers followed by * instantiating and registering all servlets and filters. * * This class is the central logic processor for Gitblit. All settings, user * object, and repository object operations pass through this class. * Leveraging Servlet 3 and Dagger static dependency injection allows Gitblit to * be modular and completely code-driven rather then relying on the fragility of * a web.xml descriptor and the static & monolithic design previously used. * * @author James Moger * */ @WebListener public class GitBlit extends DaggerContextListener implements IFederationManager, IGitblitManager { public class GitBlit extends DaggerContextListener { private static GitBlit gitblit; private final List<IManager> managers = new ArrayList<IManager>(); private final IStoredSettings goSettings; private final File goBaseFolder; private final List<IManager> managers = new ArrayList<IManager>(); private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(10); private final List<FederationModel> federationRegistrations = Collections .synchronizedList(new ArrayList<FederationModel>()); private final ObjectCache<Collection<GitClientApplication>> clientApplications = new ObjectCache<Collection<GitClientApplication>>(); private final Map<String, FederationModel> federationPullResults = new ConcurrentHashMap<String, FederationModel>(); private IStoredSettings settings; private FanoutService fanoutService; private GitDaemon gitDaemon; /** * Construct a Gitblit WAR/Express context. */ public GitBlit() { this.goSettings = null; this.goBaseFolder = null; gitblit = this; } /** * Construct a Gitblit GO context. * * @param settings * @param baseFolder */ public GitBlit(IStoredSettings settings, File baseFolder) { this.goSettings = settings; this.goBaseFolder = baseFolder; @@ -148,20 +97,13 @@ } /** * Returns the Gitblit singleton. * This method is only used for unit and integration testing. * * @return gitblit singleton * @param managerClass * @return a manager */ public static GitBlit self() { return gitblit; } @SuppressWarnings("unchecked") public static <X> X getManager(Class<X> managerClass) { if (managerClass.isAssignableFrom(GitBlit.class)) { return (X) gitblit; } public static <X extends IManager> X getManager(Class<X> managerClass) { for (IManager manager : gitblit.managers) { if (managerClass.isAssignableFrom(manager.getClass())) { return (X) manager; @@ -171,786 +113,15 @@ } /** * Returns the path of the proposals folder. This method checks to see if * Gitblit is running on a cloud service and may return an adjusted path. * * @return the proposals folder path * Returns Gitblit's Dagger injection modules. */ @Override public File getProposalsFolder() { return getManager(IRuntimeManager.class).getFileOrFolder(Keys.federation.proposalsFolder, "${baseFolder}/proposals"); protected Object [] getModules() { return new Object [] { new DaggerModule() }; } /** * 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)); } } // git daemon url String gitDaemonUrl = getGitDaemonUrl(request, user, repository); if (!StringUtils.isEmpty(gitDaemonUrl)) { AccessPermission permission = getGitDaemonAccessPermission(user, repository); if (permission.exceeds(AccessPermission.NONE)) { list.add(new RepositoryUrl(gitDaemonUrl, 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.GIT_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(); } protected String getGitDaemonUrl(HttpServletRequest request, UserModel user, RepositoryModel repository) { if (gitDaemon != null) { String bindInterface = settings.getString(Keys.git.daemonBindInterface, "localhost"); if (bindInterface.equals("localhost") && (!request.getServerName().equals("localhost") && !request.getServerName().equals("127.0.0.1"))) { // git daemon is bound to localhost and the request is from elsewhere return null; } if (user.canClone(repository)) { String servername = request.getServerName(); String url = gitDaemon.formatUrl(servername, repository.name); return url; } } return null; } protected AccessPermission getGitDaemonAccessPermission(UserModel user, RepositoryModel repository) { if (gitDaemon != null && user.canClone(repository)) { AccessPermission gitDaemonPermission = user.getRepositoryPermission(repository).permission; if (gitDaemonPermission.atLeast(AccessPermission.CLONE)) { if (repository.accessRestriction.atLeast(AccessRestrictionType.CLONE)) { // can not authenticate clone via anonymous git protocol gitDaemonPermission = AccessPermission.NONE; } else if (repository.accessRestriction.atLeast(AccessRestrictionType.PUSH)) { // can not authenticate push via anonymous git protocol gitDaemonPermission = AccessPermission.CLONE; } else { // normal user permission } } return gitDaemonPermission; } return AccessPermission.NONE; } /** * 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(getManager(IRuntimeManager.class).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; } /** * Open a file resource using the Servlet container. * @param file to open * @return InputStream of the opened file * @throws ResourceStreamNotFoundException */ public InputStream getResourceAsStream(String file) throws ResourceStreamNotFoundException { ContextRelativeResource res = WicketUtils.getResource(file); return res.getResourceStream().getInputStream(); } @Override public UserModel getFederationUser() { // the federation user is an administrator UserModel federationUser = new UserModel(Constants.FEDERATION_USER); federationUser.canAdmin = true; return federationUser; } /** * Adds/updates a complete user object keyed by username. This method allows * for renaming a user. * * @see IUserService.updateUserModel(String, UserModel) * @param username * @param user * @param isCreate * @throws GitBlitException */ @Override public void updateUserModel(String username, UserModel user, boolean isCreate) throws GitBlitException { if (!username.equalsIgnoreCase(user.username)) { if (getManager(IUserManager.class).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 : getManager(IRepositoryManager.class).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(); getManager(IRepositoryManager.class).updateRepositoryModel(oldRepositoryName, model, false); } else if (model.isOwner(username)) { // common/shared repo model.addOwner(user.username); getManager(IRepositoryManager.class).updateRepositoryModel(model.name, model, false); } } } if (!getManager(IUserManager.class).updateUserModel(username, user)) { throw new GitBlitException(isCreate ? "Failed to add user!" : "Failed to update user!"); } } /** * Updates the TeamModel object for the specified name. * * @param teamname * @param team * @param isCreate */ @Override public void updateTeamModel(String teamname, TeamModel team, boolean isCreate) throws GitBlitException { if (!teamname.equalsIgnoreCase(team.name)) { if (getManager(IUserManager.class).getTeamModel(team.name) != null) { throw new GitBlitException(MessageFormat.format( "Failed to rename ''{0}'' because ''{1}'' already exists.", teamname, team.name)); } } if (!getManager(IUserManager.class).updateTeamModel(teamname, team)) { throw new GitBlitException(isCreate ? "Failed to add team!" : "Failed to update team!"); } } /** * Returns Gitblit's scheduled executor service for scheduling tasks. * * @return scheduledExecutor */ public ScheduledExecutorService executor() { return scheduledExecutor; } @Override public boolean canFederate() { String passphrase = settings.getString(Keys.federation.passphrase, ""); return !StringUtils.isEmpty(passphrase); } /** * Configures this Gitblit instance to pull any registered federated gitblit * instances. */ private void configureFederation() { boolean validPassphrase = true; String passphrase = settings.getString(Keys.federation.passphrase, ""); if (StringUtils.isEmpty(passphrase)) { logger.warn("Federation passphrase is blank! This server can not be PULLED from."); validPassphrase = false; } if (validPassphrase) { // standard tokens for (FederationToken tokenType : FederationToken.values()) { logger.info(MessageFormat.format("Federation {0} token = {1}", tokenType.name(), getFederationToken(tokenType))); } // federation set tokens for (String set : settings.getStrings(Keys.federation.sets)) { logger.info(MessageFormat.format("Federation Set {0} token = {1}", set, getFederationToken(set))); } } // Schedule the federation executor List<FederationModel> registrations = getFederationRegistrations(); if (registrations.size() > 0) { FederationPullExecutor executor = new FederationPullExecutor(registrations, true); scheduledExecutor.schedule(executor, 1, TimeUnit.MINUTES); } } /** * Returns the list of federated gitblit instances that this instance will * try to pull. * * @return list of registered gitblit instances */ @Override public List<FederationModel> getFederationRegistrations() { if (federationRegistrations.isEmpty()) { federationRegistrations.addAll(FederationUtils.getFederationRegistrations(settings)); } return federationRegistrations; } /** * Retrieve the specified federation registration. * * @param name * the name of the registration * @return a federation registration */ @Override public FederationModel getFederationRegistration(String url, String name) { // check registrations for (FederationModel r : getFederationRegistrations()) { if (r.name.equals(name) && r.url.equals(url)) { return r; } } // check the results for (FederationModel r : getFederationResultRegistrations()) { if (r.name.equals(name) && r.url.equals(url)) { return r; } } return null; } /** * Returns the list of federation sets. * * @return list of federation sets */ @Override public List<FederationSet> getFederationSets(String gitblitUrl) { List<FederationSet> list = new ArrayList<FederationSet>(); // generate standard tokens for (FederationToken type : FederationToken.values()) { FederationSet fset = new FederationSet(type.toString(), type, getFederationToken(type)); fset.repositories = getRepositories(gitblitUrl, fset.token); list.add(fset); } // generate tokens for federation sets for (String set : settings.getStrings(Keys.federation.sets)) { FederationSet fset = new FederationSet(set, FederationToken.REPOSITORIES, getFederationToken(set)); fset.repositories = getRepositories(gitblitUrl, fset.token); list.add(fset); } return list; } /** * Returns the list of possible federation tokens for this Gitblit instance. * * @return list of federation tokens */ @Override public List<String> getFederationTokens() { List<String> tokens = new ArrayList<String>(); // generate standard tokens for (FederationToken type : FederationToken.values()) { tokens.add(getFederationToken(type)); } // generate tokens for federation sets for (String set : settings.getStrings(Keys.federation.sets)) { tokens.add(getFederationToken(set)); } return tokens; } /** * Returns the specified federation token for this Gitblit instance. * * @param type * @return a federation token */ @Override public String getFederationToken(FederationToken type) { return getFederationToken(type.name()); } /** * Returns the specified federation token for this Gitblit instance. * * @param value * @return a federation token */ @Override public String getFederationToken(String value) { String passphrase = settings.getString(Keys.federation.passphrase, ""); return StringUtils.getSHA1(passphrase + "-" + value); } /** * Compares the provided token with this Gitblit instance's tokens and * determines if the requested permission may be granted to the token. * * @param req * @param token * @return true if the request can be executed */ @Override public boolean validateFederationRequest(FederationRequest req, String token) { String all = getFederationToken(FederationToken.ALL); String unr = getFederationToken(FederationToken.USERS_AND_REPOSITORIES); String jur = getFederationToken(FederationToken.REPOSITORIES); switch (req) { case PULL_REPOSITORIES: return token.equals(all) || token.equals(unr) || token.equals(jur); case PULL_USERS: case PULL_TEAMS: return token.equals(all) || token.equals(unr); case PULL_SETTINGS: case PULL_SCRIPTS: return token.equals(all); default: break; } return false; } /** * Acknowledge and cache the status of a remote Gitblit instance. * * @param identification * the identification of the pulling Gitblit instance * @param registration * the registration from the pulling Gitblit instance * @return true if acknowledged */ @Override public boolean acknowledgeFederationStatus(String identification, FederationModel registration) { // reset the url to the identification of the pulling Gitblit instance registration.url = identification; String id = identification; if (!StringUtils.isEmpty(registration.folder)) { id += "-" + registration.folder; } federationPullResults.put(id, registration); return true; } /** * Returns the list of registration results. * * @return the list of registration results */ @Override public List<FederationModel> getFederationResultRegistrations() { return new ArrayList<FederationModel>(federationPullResults.values()); } /** * Submit a federation proposal. The proposal is cached locally and the * Gitblit administrator(s) are notified via email. * * @param proposal * the proposal * @param gitblitUrl * the url of your gitblit instance to send an email to * administrators * @return true if the proposal was submitted */ @Override public boolean submitFederationProposal(FederationProposal proposal, String gitblitUrl) { // convert proposal to json String json = JsonUtils.toJsonString(proposal); try { // make the proposals folder File proposalsFolder = getProposalsFolder(); proposalsFolder.mkdirs(); // cache json to a file File file = new File(proposalsFolder, proposal.token + Constants.PROPOSAL_EXT); com.gitblit.utils.FileUtils.writeContent(file, json); } catch (Exception e) { logger.error(MessageFormat.format("Failed to cache proposal from {0}", proposal.url), e); } // send an email, if possible getManager(INotificationManager.class).sendMailToAdministrators("Federation proposal from " + proposal.url, "Please review the proposal @ " + gitblitUrl + "/proposal/" + proposal.token); return true; } /** * Returns the list of pending federation proposals * * @return list of federation proposals */ @Override public List<FederationProposal> getPendingFederationProposals() { List<FederationProposal> list = new ArrayList<FederationProposal>(); File folder = getProposalsFolder(); if (folder.exists()) { File[] files = folder.listFiles(new FileFilter() { @Override public boolean accept(File file) { return file.isFile() && file.getName().toLowerCase().endsWith(Constants.PROPOSAL_EXT); } }); for (File file : files) { String json = com.gitblit.utils.FileUtils.readContent(file, null); FederationProposal proposal = JsonUtils.fromJsonString(json, FederationProposal.class); list.add(proposal); } } return list; } /** * Get repositories for the specified token. * * @param gitblitUrl * the base url of this gitblit instance * @param token * the federation token * @return a map of <cloneurl, RepositoryModel> */ @Override public Map<String, RepositoryModel> getRepositories(String gitblitUrl, String token) { Map<String, String> federationSets = new HashMap<String, String>(); for (String set : settings.getStrings(Keys.federation.sets)) { federationSets.put(getFederationToken(set), set); } // Determine the Gitblit clone url StringBuilder sb = new StringBuilder(); sb.append(gitblitUrl); sb.append(Constants.GIT_PATH); sb.append("{0}"); String cloneUrl = sb.toString(); // Retrieve all available repositories UserModel user = getFederationUser(); List<RepositoryModel> list = getManager(IRepositoryManager.class).getRepositoryModels(user); // create the [cloneurl, repositoryModel] map Map<String, RepositoryModel> repositories = new HashMap<String, RepositoryModel>(); for (RepositoryModel model : list) { // by default, setup the url for THIS repository String url = MessageFormat.format(cloneUrl, model.name); switch (model.federationStrategy) { case EXCLUDE: // skip this repository continue; case FEDERATE_ORIGIN: // federate the origin, if it is defined if (!StringUtils.isEmpty(model.origin)) { url = model.origin; } break; default: break; } if (federationSets.containsKey(token)) { // include repositories only for federation set String set = federationSets.get(token); if (model.federationSets.contains(set)) { repositories.put(url, model); } } else { // standard federation token for ALL repositories.put(url, model); } } return repositories; } /** * Creates a proposal from the token. * * @param gitblitUrl * the url of this Gitblit instance * @param token * @return a potential proposal */ @Override public FederationProposal createFederationProposal(String gitblitUrl, String token) { FederationToken tokenType = FederationToken.REPOSITORIES; for (FederationToken type : FederationToken.values()) { if (token.equals(getFederationToken(type))) { tokenType = type; break; } } Map<String, RepositoryModel> repositories = getRepositories(gitblitUrl, token); FederationProposal proposal = new FederationProposal(gitblitUrl, tokenType, token, repositories); return proposal; } /** * Returns the proposal identified by the supplied token. * * @param token * @return the specified proposal or null */ @Override public FederationProposal getPendingFederationProposal(String token) { List<FederationProposal> list = getPendingFederationProposals(); for (FederationProposal proposal : list) { if (proposal.token.equals(token)) { return proposal; } } return null; } /** * Deletes a pending federation proposal. * * @param a * proposal * @return true if the proposal was deleted */ @Override public boolean deletePendingFederationProposal(FederationProposal proposal) { File folder = getProposalsFolder(); File file = new File(folder, proposal.token + Constants.PROPOSAL_EXT); return file.delete(); } /** * 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 ServerSettings loadSettingModels(ServerSettings settingsModel) { // this entire "supports" concept will go away with user service refactoring UserModel externalUser = new UserModel(Constants.EXTERNAL_ACCOUNT); externalUser.password = Constants.EXTERNAL_ACCOUNT; IUserManager userManager = getManager(IUserManager.class); settingsModel.supportsCredentialChanges = userManager.supportsCredentialChanges(externalUser); settingsModel.supportsDisplayNameChanges = userManager.supportsDisplayNameChanges(externalUser); settingsModel.supportsEmailAddressChanges = userManager.supportsEmailAddressChanges(externalUser); settingsModel.supportsTeamMembershipChanges = userManager.supportsTeamMembershipChanges(externalUser); 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"); } return settingsModel; } protected void configureFanout() { // startup Fanout PubSub service if (settings.getInteger(Keys.fanout.port, 0) > 0) { String bindInterface = settings.getString(Keys.fanout.bindInterface, null); int port = settings.getInteger(Keys.fanout.port, FanoutService.DEFAULT_PORT); boolean useNio = settings.getBoolean(Keys.fanout.useNio, true); int limit = settings.getInteger(Keys.fanout.connectionLimit, 0); if (useNio) { if (StringUtils.isEmpty(bindInterface)) { fanoutService = new FanoutNioService(port); } else { fanoutService = new FanoutNioService(bindInterface, port); } } else { if (StringUtils.isEmpty(bindInterface)) { fanoutService = new FanoutSocketService(port); } else { fanoutService = new FanoutSocketService(bindInterface, port); } } fanoutService.setConcurrentConnectionLimit(limit); fanoutService.setAllowAllChannelAnnouncements(false); fanoutService.start(); } } protected void configureGitDaemon() { int port = settings.getInteger(Keys.git.daemonPort, 0); String bindInterface = settings.getString(Keys.git.daemonBindInterface, "localhost"); if (port > 0) { try { // HACK temporary pending manager separation and injection Gitblit gitblit = new Gitblit( getManager(IRuntimeManager.class), getManager(INotificationManager.class), getManager(IUserManager.class), getManager(ISessionManager.class), getManager(IRepositoryManager.class), getManager(IProjectManager.class), this, this); gitDaemon = new GitDaemon(gitblit); gitDaemon.start(); } catch (IOException e) { gitDaemon = null; logger.error(MessageFormat.format("Failed to start Git daemon on {0}:{1,number,0}", bindInterface, port), e); } } } protected final Logger getLogger() { return logger; } protected final ScheduledExecutorService getScheduledExecutor() { return scheduledExecutor; } /** * Configure Gitblit from the web.xml, if no configuration has already been * specified. * * @see ServletContextListener.contextInitialize(ServletContextEvent) * Prepare runtime settings and start all manager instances. */ @Override protected void beforeServletInjection(ServletContext context) { @@ -958,12 +129,10 @@ // create the runtime settings object IStoredSettings runtimeSettings = injector.get(IStoredSettings.class); this.settings = runtimeSettings; // XXX remove me eventually final File baseFolder; if (goSettings != null) { // Gitblit GO logger.debug("configuring Gitblit GO"); baseFolder = configureGO(context, goSettings, goBaseFolder, runtimeSettings); } else { // servlet container @@ -973,11 +142,9 @@ if (!StringUtils.isEmpty(System.getenv("OPENSHIFT_DATA_DIR"))) { // RedHat OpenShift logger.debug("configuring Gitblit Express"); baseFolder = configureExpress(context, webxmlSettings, contextFolder, runtimeSettings); } else { // standard WAR logger.debug("configuring Gitblit WAR"); baseFolder = configureWAR(context, webxmlSettings, contextFolder, runtimeSettings); } @@ -985,27 +152,85 @@ ContainerUtils.CVE_2007_0450.test(runtimeSettings); } // Runtime manager is a container for settings and other parameters IRuntimeManager runtime = startManager(injector, IRuntimeManager.class); // Manually configure IRuntimeManager logManager(IRuntimeManager.class); IRuntimeManager runtime = injector.get(IRuntimeManager.class); runtime.setBaseFolder(baseFolder); runtime.getStatus().isGO = goSettings != null; runtime.getStatus().servletContainer = context.getServerInfo(); runtime.start(); managers.add(runtime); // start all other managers startManager(injector, INotificationManager.class); startManager(injector, IUserManager.class); startManager(injector, ISessionManager.class); startManager(injector, IRepositoryManager.class); startManager(injector, IProjectManager.class); startManager(injector, IGitblitManager.class); startManager(injector, IFederationManager.class); startManager(injector, IServicesManager.class); logger.info("Gitblit base folder = " + baseFolder.getAbsolutePath()); loadSettingModels(runtime.getSettingsModel()); if (true/*startFederation*/) { configureFederation(); logger.info(""); logger.info("All managers started."); logger.info(""); } configureFanout(); configureGitDaemon(); protected <X extends IManager> X startManager(ObjectGraph injector, Class<X> clazz) { logManager(clazz); X x = injector.get(clazz); x.start(); managers.add(x); return x; } protected void logManager(Class<? extends IManager> clazz) { logger.info(""); logger.info("----[{}]----", clazz.getName()); } /** * Instantiate and inject all filters and servlets into the container using * the servlet 3 specification. */ @Override protected void injectServlets(ServletContext context) { // access restricted servlets serve(context, Constants.GIT_PATH, GitServlet.class, GitFilter.class); serve(context, Constants.PAGES, PagesServlet.class, PagesFilter.class); serve(context, Constants.RPC_PATH, RpcServlet.class, RpcFilter.class); serve(context, Constants.ZIP_PATH, DownloadZipServlet.class, DownloadZipFilter.class); serve(context, Constants.SYNDICATION_PATH, SyndicationServlet.class, SyndicationFilter.class); // servlets serve(context, Constants.FEDERATION_PATH, FederationServlet.class); serve(context, Constants.SPARKLESHARE_INVITE_PATH, SparkleShareInviteServlet.class); serve(context, Constants.BRANCH_GRAPH_PATH, BranchGraphServlet.class); file(context, "/robots.txt", RobotsTxtServlet.class); file(context, "/logo.png", LogoServlet.class); // optional force basic authentication filter(context, "/*", EnforceAuthenticationFilter.class, null); // Wicket String toIgnore = StringUtils.flattenStrings(getRegisteredPaths(), ","); Map<String, String> params = new HashMap<String, String>(); params.put(GitblitWicketFilter.FILTER_MAPPING_PARAM, "/*"); params.put(GitblitWicketFilter.IGNORE_PATHS_PARAM, toIgnore); filter(context, "/*", GitblitWicketFilter.class, params); } /** * Gitblit is being shutdown either because the servlet container is * shutting down or because the servlet container is re-deploying Gitblit. */ @Override protected void destroyContext(ServletContext context) { logger.info("Gitblit context destroyed by servlet container."); for (IManager manager : managers) { logger.debug("stopping {}", manager.getClass().getSimpleName()); manager.stop(); } } /** @@ -1022,6 +247,8 @@ IStoredSettings goSettings, File goBaseFolder, IStoredSettings runtimeSettings) { logger.debug("configuring Gitblit GO"); // merge the stored settings into the runtime settings // @@ -1049,6 +276,7 @@ IStoredSettings runtimeSettings) { // Gitblit is running in a standard servlet container logger.debug("configuring Gitblit WAR"); logger.info("WAR contextFolder is " + ((contextFolder != null) ? contextFolder.getAbsolutePath() : "<empty>")); String path = webxmlSettings.getString(Constants.baseFolder, Constants.contextFolder$ + "/WEB-INF/data"); @@ -1112,6 +340,7 @@ IStoredSettings runtimeSettings) { // Gitblit is running in OpenShift/JBoss logger.debug("configuring Gitblit Express"); String openShift = System.getenv("OPENSHIFT_DATA_DIR"); File base = new File(openShift); logger.info("EXPRESS contextFolder is " + contextFolder.getAbsolutePath()); @@ -1193,142 +422,5 @@ } } } } /** * Gitblit is being shutdown either because the servlet container is * shutting down or because the servlet container is re-deploying Gitblit. */ @Override protected void destroyContext(ServletContext context) { logger.info("Gitblit context destroyed by servlet container."); for (IManager manager : managers) { logger.debug("stopping {}", manager.getClass().getSimpleName()); manager.stop(); } scheduledExecutor.shutdownNow(); if (fanoutService != null) { fanoutService.stop(); } if (gitDaemon != null) { gitDaemon.stop(); } } /** * 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}", getManager(IRepositoryManager.class).getRepositoriesFolder().getAbsolutePath(), repository.name); // clone the repository try { JGitUtils.cloneRepository(getManager(IRepositoryManager.class).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); getManager(IRepositoryManager.class).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 = getManager(IUserManager.class).getUserModel(owner); if (originOwner != null) { originOwner.setRepositoryPermission(cloneName, AccessPermission.CLONE); updateUserModel(originOwner.username, originOwner, false); } } } // grant origin's user list clone permission to fork List<String> users = getManager(IRepositoryManager.class).getRepositoryUsers(repository); List<UserModel> cloneUsers = new ArrayList<UserModel>(); for (String name : users) { if (!name.equalsIgnoreCase(user.username)) { UserModel cloneUser = getManager(IUserManager.class).getUserModel(name); if (cloneUser.canClone(repository)) { // origin user can clone origin, grant clone access to fork cloneUser.setRepositoryPermission(cloneName, AccessPermission.CLONE); } cloneUsers.add(cloneUser); } } getManager(IUserManager.class).updateUserModels(cloneUsers); // grant origin's team list clone permission to fork List<String> teams = getManager(IRepositoryManager.class).getRepositoryTeams(repository); List<TeamModel> cloneTeams = new ArrayList<TeamModel>(); for (String name : teams) { TeamModel cloneTeam = getManager(IUserManager.class).getTeamModel(name); if (cloneTeam.canClone(repository)) { // origin team can clone origin, grant clone access to fork cloneTeam.setRepositoryPermission(cloneName, AccessPermission.CLONE); } cloneTeams.add(cloneTeam); } getManager(IUserManager.class).updateTeamModels(cloneTeams); // add this clone to the cached model getManager(IRepositoryManager.class).addToCachedRepositoryList(cloneModel); return cloneModel; } @Override protected Object [] getModules() { return new Object [] { new DaggerModule(this) }; } protected <X extends IManager> X startManager(ObjectGraph injector, Class<X> clazz) { logger.debug("injecting and starting {}", clazz.getSimpleName()); X x = injector.get(clazz); x.setup(); managers.add(x); return x; } /** * Instantiate and inject all filters and servlets into the container using * the servlet 3 specification. */ @Override protected void injectServlets(ServletContext context) { // access restricted servlets serve(context, Constants.GIT_PATH, GitServlet.class, GitFilter.class); serve(context, Constants.PAGES, PagesServlet.class, PagesFilter.class); serve(context, Constants.RPC_PATH, RpcServlet.class, RpcFilter.class); serve(context, Constants.ZIP_PATH, DownloadZipServlet.class, DownloadZipFilter.class); serve(context, Constants.SYNDICATION_PATH, SyndicationServlet.class, SyndicationFilter.class); // servlets serve(context, Constants.FEDERATION_PATH, FederationServlet.class); serve(context, Constants.SPARKLESHARE_INVITE_PATH, SparkleShareInviteServlet.class); serve(context, Constants.BRANCH_GRAPH_PATH, BranchGraphServlet.class); file(context, "/robots.txt", RobotsTxtServlet.class); file(context, "/logo.png", LogoServlet.class); // optional force basic authentication filter(context, "/*", EnforceAuthenticationFilter.class, null); // Wicket String toIgnore = StringUtils.flattenStrings(getRegisteredPaths(), ","); Map<String, String> params = new HashMap<String, String>(); params.put(GitblitWicketFilter.FILTER_MAPPING_PARAM, "/*"); params.put(GitblitWicketFilter.IGNORE_PATHS_PARAM, toIgnore); filter(context, "/*", GitblitWicketFilter.class, params); } } src/main/java/com/gitblit/Gitblit.java
@@ -22,8 +22,6 @@ import java.util.Map; import java.util.TimeZone; import javax.inject.Inject; import javax.inject.Singleton; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -33,7 +31,6 @@ import com.gitblit.Constants.FederationToken; import com.gitblit.manager.IFederationManager; import com.gitblit.manager.IGitblitManager; import com.gitblit.manager.IManager; import com.gitblit.manager.INotificationManager; import com.gitblit.manager.IProjectManager; import com.gitblit.manager.IRepositoryManager; @@ -65,7 +62,6 @@ * @author James Moger * */ @Singleton public class Gitblit implements IRuntimeManager, INotificationManager, IUserManager, @@ -87,11 +83,10 @@ private final IProjectManager projectManager; private final IFederationManager federationManager; private final IGitblitManager gitblitManager; @Inject private final IFederationManager federationManager; public Gitblit( IRuntimeManager runtimeManager, INotificationManager notificationManager, @@ -99,8 +94,8 @@ ISessionManager sessionManager, IRepositoryManager repositoryManager, IProjectManager projectManager, IFederationManager federationManager, IGitblitManager gitblitManager) { IGitblitManager gitblitManager, IFederationManager federationManager) { this.runtimeManager = runtimeManager; this.notificationManager = notificationManager; @@ -108,17 +103,17 @@ this.sessionManager = sessionManager; this.repositoryManager = repositoryManager; this.projectManager = projectManager; this.federationManager = federationManager; this.gitblitManager = gitblitManager; this.federationManager = federationManager; } @Override public IManager setup() { public Gitblit start() { return this; } @Override public IManager stop() { public Gitblit stop() { return this; } src/main/java/com/gitblit/manager/FederationManager.java
New file @@ -0,0 +1,454 @@ /* * 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.File; import java.io.FileFilter; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gitblit.Constants; import com.gitblit.Constants.FederationRequest; import com.gitblit.Constants.FederationToken; 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.RepositoryModel; import com.gitblit.models.UserModel; import com.gitblit.utils.FederationUtils; import com.gitblit.utils.JsonUtils; import com.gitblit.utils.StringUtils; /** * Federation manager controls all aspects of handling federation sets, tokens, * and proposals. * * @author James Moger * */ public class FederationManager implements IFederationManager { private final Logger logger = LoggerFactory.getLogger(getClass()); private final List<FederationModel> federationRegistrations = Collections .synchronizedList(new ArrayList<FederationModel>()); private final Map<String, FederationModel> federationPullResults = new ConcurrentHashMap<String, FederationModel>(); private final IStoredSettings settings; private final IRuntimeManager runtimeManager; private final INotificationManager notificationManager; private final IRepositoryManager repositoryManager; public FederationManager( IRuntimeManager runtimeManager, INotificationManager notificationManager, IUserManager userManager, IRepositoryManager repositoryManager) { this.settings = runtimeManager.getSettings(); this.runtimeManager = runtimeManager; this.notificationManager = notificationManager; this.repositoryManager = repositoryManager; } @Override public FederationManager start() { return this; } @Override public FederationManager stop() { return this; } /** * Returns the path of the proposals folder. This method checks to see if * Gitblit is running on a cloud service and may return an adjusted path. * * @return the proposals folder path */ @Override public File getProposalsFolder() { return runtimeManager.getFileOrFolder(Keys.federation.proposalsFolder, "${baseFolder}/proposals"); } @Override public UserModel getFederationUser() { // the federation user is an administrator UserModel federationUser = new UserModel(Constants.FEDERATION_USER); federationUser.canAdmin = true; return federationUser; } @Override public boolean canFederate() { String passphrase = settings.getString(Keys.federation.passphrase, ""); return !StringUtils.isEmpty(passphrase); } /** * Returns the list of federated gitblit instances that this instance will * try to pull. * * @return list of registered gitblit instances */ @Override public List<FederationModel> getFederationRegistrations() { if (federationRegistrations.isEmpty()) { federationRegistrations.addAll(FederationUtils.getFederationRegistrations(settings)); } return federationRegistrations; } /** * Retrieve the specified federation registration. * * @param name * the name of the registration * @return a federation registration */ @Override public FederationModel getFederationRegistration(String url, String name) { // check registrations for (FederationModel r : getFederationRegistrations()) { if (r.name.equals(name) && r.url.equals(url)) { return r; } } // check the results for (FederationModel r : getFederationResultRegistrations()) { if (r.name.equals(name) && r.url.equals(url)) { return r; } } return null; } /** * Returns the list of federation sets. * * @return list of federation sets */ @Override public List<FederationSet> getFederationSets(String gitblitUrl) { List<FederationSet> list = new ArrayList<FederationSet>(); // generate standard tokens for (FederationToken type : FederationToken.values()) { FederationSet fset = new FederationSet(type.toString(), type, getFederationToken(type)); fset.repositories = getRepositories(gitblitUrl, fset.token); list.add(fset); } // generate tokens for federation sets for (String set : settings.getStrings(Keys.federation.sets)) { FederationSet fset = new FederationSet(set, FederationToken.REPOSITORIES, getFederationToken(set)); fset.repositories = getRepositories(gitblitUrl, fset.token); list.add(fset); } return list; } /** * Returns the list of possible federation tokens for this Gitblit instance. * * @return list of federation tokens */ @Override public List<String> getFederationTokens() { List<String> tokens = new ArrayList<String>(); // generate standard tokens for (FederationToken type : FederationToken.values()) { tokens.add(getFederationToken(type)); } // generate tokens for federation sets for (String set : settings.getStrings(Keys.federation.sets)) { tokens.add(getFederationToken(set)); } return tokens; } /** * Returns the specified federation token for this Gitblit instance. * * @param type * @return a federation token */ @Override public String getFederationToken(FederationToken type) { return getFederationToken(type.name()); } /** * Returns the specified federation token for this Gitblit instance. * * @param value * @return a federation token */ @Override public String getFederationToken(String value) { String passphrase = settings.getString(Keys.federation.passphrase, ""); return StringUtils.getSHA1(passphrase + "-" + value); } /** * Compares the provided token with this Gitblit instance's tokens and * determines if the requested permission may be granted to the token. * * @param req * @param token * @return true if the request can be executed */ @Override public boolean validateFederationRequest(FederationRequest req, String token) { String all = getFederationToken(FederationToken.ALL); String unr = getFederationToken(FederationToken.USERS_AND_REPOSITORIES); String jur = getFederationToken(FederationToken.REPOSITORIES); switch (req) { case PULL_REPOSITORIES: return token.equals(all) || token.equals(unr) || token.equals(jur); case PULL_USERS: case PULL_TEAMS: return token.equals(all) || token.equals(unr); case PULL_SETTINGS: case PULL_SCRIPTS: return token.equals(all); default: break; } return false; } /** * Acknowledge and cache the status of a remote Gitblit instance. * * @param identification * the identification of the pulling Gitblit instance * @param registration * the registration from the pulling Gitblit instance * @return true if acknowledged */ @Override public boolean acknowledgeFederationStatus(String identification, FederationModel registration) { // reset the url to the identification of the pulling Gitblit instance registration.url = identification; String id = identification; if (!StringUtils.isEmpty(registration.folder)) { id += "-" + registration.folder; } federationPullResults.put(id, registration); return true; } /** * Returns the list of registration results. * * @return the list of registration results */ @Override public List<FederationModel> getFederationResultRegistrations() { return new ArrayList<FederationModel>(federationPullResults.values()); } /** * Submit a federation proposal. The proposal is cached locally and the * Gitblit administrator(s) are notified via email. * * @param proposal * the proposal * @param gitblitUrl * the url of your gitblit instance to send an email to * administrators * @return true if the proposal was submitted */ @Override public boolean submitFederationProposal(FederationProposal proposal, String gitblitUrl) { // convert proposal to json String json = JsonUtils.toJsonString(proposal); try { // make the proposals folder File proposalsFolder = getProposalsFolder(); proposalsFolder.mkdirs(); // cache json to a file File file = new File(proposalsFolder, proposal.token + Constants.PROPOSAL_EXT); com.gitblit.utils.FileUtils.writeContent(file, json); } catch (Exception e) { logger.error(MessageFormat.format("Failed to cache proposal from {0}", proposal.url), e); } // send an email, if possible notificationManager.sendMailToAdministrators("Federation proposal from " + proposal.url, "Please review the proposal @ " + gitblitUrl + "/proposal/" + proposal.token); return true; } /** * Returns the list of pending federation proposals * * @return list of federation proposals */ @Override public List<FederationProposal> getPendingFederationProposals() { List<FederationProposal> list = new ArrayList<FederationProposal>(); File folder = getProposalsFolder(); if (folder.exists()) { File[] files = folder.listFiles(new FileFilter() { @Override public boolean accept(File file) { return file.isFile() && file.getName().toLowerCase().endsWith(Constants.PROPOSAL_EXT); } }); for (File file : files) { String json = com.gitblit.utils.FileUtils.readContent(file, null); FederationProposal proposal = JsonUtils.fromJsonString(json, FederationProposal.class); list.add(proposal); } } return list; } /** * Get repositories for the specified token. * * @param gitblitUrl * the base url of this gitblit instance * @param token * the federation token * @return a map of <cloneurl, RepositoryModel> */ @Override public Map<String, RepositoryModel> getRepositories(String gitblitUrl, String token) { Map<String, String> federationSets = new HashMap<String, String>(); for (String set : settings.getStrings(Keys.federation.sets)) { federationSets.put(getFederationToken(set), set); } // Determine the Gitblit clone url StringBuilder sb = new StringBuilder(); sb.append(gitblitUrl); sb.append(Constants.GIT_PATH); sb.append("{0}"); String cloneUrl = sb.toString(); // Retrieve all available repositories UserModel user = getFederationUser(); List<RepositoryModel> list = repositoryManager.getRepositoryModels(user); // create the [cloneurl, repositoryModel] map Map<String, RepositoryModel> repositories = new HashMap<String, RepositoryModel>(); for (RepositoryModel model : list) { // by default, setup the url for THIS repository String url = MessageFormat.format(cloneUrl, model.name); switch (model.federationStrategy) { case EXCLUDE: // skip this repository continue; case FEDERATE_ORIGIN: // federate the origin, if it is defined if (!StringUtils.isEmpty(model.origin)) { url = model.origin; } break; default: break; } if (federationSets.containsKey(token)) { // include repositories only for federation set String set = federationSets.get(token); if (model.federationSets.contains(set)) { repositories.put(url, model); } } else { // standard federation token for ALL repositories.put(url, model); } } return repositories; } /** * Creates a proposal from the token. * * @param gitblitUrl * the url of this Gitblit instance * @param token * @return a potential proposal */ @Override public FederationProposal createFederationProposal(String gitblitUrl, String token) { FederationToken tokenType = FederationToken.REPOSITORIES; for (FederationToken type : FederationToken.values()) { if (token.equals(getFederationToken(type))) { tokenType = type; break; } } Map<String, RepositoryModel> repositories = getRepositories(gitblitUrl, token); FederationProposal proposal = new FederationProposal(gitblitUrl, tokenType, token, repositories); return proposal; } /** * Returns the proposal identified by the supplied token. * * @param token * @return the specified proposal or null */ @Override public FederationProposal getPendingFederationProposal(String token) { List<FederationProposal> list = getPendingFederationProposals(); for (FederationProposal proposal : list) { if (proposal.token.equals(token)) { return proposal; } } return null; } /** * Deletes a pending federation proposal. * * @param a * proposal * @return true if the proposal was deleted */ @Override public boolean deletePendingFederationProposal(FederationProposal proposal) { File folder = getProposalsFolder(); File file = new File(folder, proposal.token + Constants.PROPOSAL_EXT); return file.delete(); } } src/main/java/com/gitblit/manager/GitblitManager.java
New file @@ -0,0 +1,463 @@ /* * 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 javax.servlet.http.HttpServletRequest; 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.GitBlitException; import com.gitblit.IStoredSettings; import com.gitblit.Keys; import com.gitblit.models.GitClientApplication; import com.gitblit.models.RepositoryModel; import com.gitblit.models.RepositoryUrl; import com.gitblit.models.ServerSettings; 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; public class GitblitManager implements IGitblitManager { 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 IUserManager userManager; private final IRepositoryManager repositoryManager; public GitblitManager( IRuntimeManager runtimeManager, IUserManager userManager, IRepositoryManager repositoryManager) { this.settings = runtimeManager.getSettings(); this.runtimeManager = runtimeManager; this.userManager = userManager; this.repositoryManager = repositoryManager; } @Override public GitblitManager start() { loadSettingModels(runtimeManager.getSettingsModel()); return this; } @Override public GitblitManager stop() { return this; } /** * 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) { // this entire "supports" concept will go away with user service refactoring UserModel externalUser = new UserModel(Constants.EXTERNAL_ACCOUNT); externalUser.password = Constants.EXTERNAL_ACCOUNT; settingsModel.supportsCredentialChanges = userManager.supportsCredentialChanges(externalUser); settingsModel.supportsDisplayNameChanges = userManager.supportsDisplayNameChanges(externalUser); settingsModel.supportsEmailAddressChanges = userManager.supportsEmailAddressChanges(externalUser); settingsModel.supportsTeamMembershipChanges = userManager.supportsTeamMembershipChanges(externalUser); 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"); } } /** * 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)); } } // git daemon url String gitDaemonUrl = getGitDaemonUrl(request, user, repository); if (!StringUtils.isEmpty(gitDaemonUrl)) { AccessPermission permission = getGitDaemonAccessPermission(user, repository); if (permission.exceeds(AccessPermission.NONE)) { list.add(new RepositoryUrl(gitDaemonUrl, 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.GIT_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(); } protected String getGitDaemonUrl(HttpServletRequest request, UserModel user, RepositoryModel repository) { // if (gitDaemon != null) { // String bindInterface = settings.getString(Keys.git.daemonBindInterface, "localhost"); // if (bindInterface.equals("localhost") // && (!request.getServerName().equals("localhost") && !request.getServerName().equals("127.0.0.1"))) { // // git daemon is bound to localhost and the request is from elsewhere // return null; // } // if (user.canClone(repository)) { // String servername = request.getServerName(); // String url = gitDaemon.formatUrl(servername, repository.name); // return url; // } // } return null; } protected AccessPermission getGitDaemonAccessPermission(UserModel user, RepositoryModel repository) { // if (gitDaemon != null && user.canClone(repository)) { // AccessPermission gitDaemonPermission = user.getRepositoryPermission(repository).permission; // if (gitDaemonPermission.atLeast(AccessPermission.CLONE)) { // if (repository.accessRestriction.atLeast(AccessRestrictionType.CLONE)) { // // can not authenticate clone via anonymous git protocol // gitDaemonPermission = AccessPermission.NONE; // } else if (repository.accessRestriction.atLeast(AccessRestrictionType.PUSH)) { // // can not authenticate push via anonymous git protocol // gitDaemonPermission = AccessPermission.CLONE; // } else { // // normal user permission // } // } // return gitDaemonPermission; // } return AccessPermission.NONE; } /** * 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; } /** * 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); updateUserModel(originOwner.username, originOwner, false); } } } // 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/updates a complete user object keyed by username. This method allows * for renaming a user. * * @see IUserService.updateUserModel(String, UserModel) * @param username * @param user * @param isCreate * @throws GitBlitException */ @Override public void updateUserModel(String username, UserModel user, boolean isCreate) 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(isCreate ? "Failed to add user!" : "Failed to update user!"); } } /** * Updates the TeamModel object for the specified name. * * @param teamname * @param team * @param isCreate */ @Override public void updateTeamModel(String teamname, TeamModel team, boolean isCreate) 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(isCreate ? "Failed to add team!" : "Failed to update team!"); } } } src/main/java/com/gitblit/manager/IFederationManager.java
@@ -27,7 +27,7 @@ import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; public interface IFederationManager { public interface IFederationManager extends IManager { /** * Returns the path of the proposals folder. This method checks to see if src/main/java/com/gitblit/manager/IGitblitManager.java
@@ -27,7 +27,7 @@ import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; public interface IGitblitManager { public interface IGitblitManager extends IManager { /** * Returns a list of repository URLs and the user access permission. src/main/java/com/gitblit/manager/IManager.java
@@ -17,7 +17,8 @@ public interface IManager { IManager setup(); IManager start(); IManager stop(); } src/main/java/com/gitblit/manager/IServicesManager.java
New file @@ -0,0 +1,21 @@ /* * 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; public interface IServicesManager extends IManager { } src/main/java/com/gitblit/manager/NotificationManager.java
@@ -58,18 +58,19 @@ } @Override public IManager setup() { public NotificationManager start() { if (mailExecutor.isReady()) { logger.info("Mail executor is scheduled to process the message queue every 2 minutes."); scheduledExecutor.scheduleAtFixedRate(mailExecutor, 1, 2, TimeUnit.MINUTES); int period = 2; logger.info("Mail service will process the queue every {} minutes.", period); scheduledExecutor.scheduleAtFixedRate(mailExecutor, 1, period, TimeUnit.MINUTES); } else { logger.warn("Mail server is not properly configured. Mail services disabled."); logger.warn("Mail service disabled."); } return this; } @Override public IManager stop() { public NotificationManager stop() { scheduledExecutor.shutdownNow(); return this; } src/main/java/com/gitblit/manager/ProjectManager.java
@@ -80,7 +80,7 @@ } @Override public IManager setup() { public ProjectManager start() { // load and cache the project metadata projectConfigs = new FileBasedConfig(runtimeManager.getFileOrFolder(Keys.web.projectsFile, "${baseFolder}/projects.conf"), FS.detect()); getProjectConfigs(); @@ -89,7 +89,7 @@ } @Override public IManager stop() { public ProjectManager stop() { return this; } src/main/java/com/gitblit/manager/RepositoryManager.java
@@ -135,8 +135,8 @@ } @Override public IManager setup() { logger.info("Git repositories folder = " + repositoriesFolder.getAbsolutePath()); public RepositoryManager start() { logger.info("Repositories folder : {}", repositoriesFolder.getAbsolutePath()); // initialize utilities String prefix = settings.getString(Keys.git.userRepositoryPrefix, "~"); @@ -147,7 +147,7 @@ // build initial repository list if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) { logger.info("Identifying available repositories..."); logger.info("Identifying repositories..."); getRepositoryList(); } @@ -161,7 +161,7 @@ } @Override public IManager stop() { public RepositoryManager stop() { scheduledExecutor.shutdownNow(); luceneExecutor.close(); gcExecutor.close(); @@ -1645,15 +1645,16 @@ protected void configureLuceneIndexing() { luceneExecutor = new LuceneExecutor(settings, this); scheduledExecutor.scheduleAtFixedRate(luceneExecutor, 1, 2, TimeUnit.MINUTES); logger.info("Lucene executor is scheduled to process indexed branches every 2 minutes."); int period = 2; scheduledExecutor.scheduleAtFixedRate(luceneExecutor, 1, period, TimeUnit.MINUTES); logger.info("Lucene will process indexed branches every {} minutes.", period); } protected void configureGarbageCollector() { // schedule gc engine gcExecutor = new GCExecutor(settings, this); if (gcExecutor.isReady()) { logger.info("GC executor is scheduled to scan repositories every 24 hours."); logger.info("Garbage Collector (GC) will scan repositories every 24 hours."); Calendar c = Calendar.getInstance(); c.set(Calendar.HOUR_OF_DAY, settings.getInteger(Keys.git.garbageCollectionHour, 0)); c.set(Calendar.MINUTE, 0); @@ -1673,6 +1674,8 @@ } logger.info(MessageFormat.format("Next scheculed GC scan is in {0}", when)); scheduledExecutor.scheduleAtFixedRate(gcExecutor, delay, 60 * 24, TimeUnit.MINUTES); } else { logger.info("Garbage Collector (GC) is disabled."); } } @@ -1685,8 +1688,10 @@ } int delay = 1; scheduledExecutor.scheduleAtFixedRate(mirrorExecutor, delay, mins, TimeUnit.MINUTES); logger.info("Mirror executor is scheduled to fetch updates every {} minutes.", mins); logger.info("Mirror service will fetch updates every {} minutes.", mins); logger.info("Next scheduled mirror fetch is in {} minutes", delay); } else { logger.info("Mirror service is disabled."); } } @@ -1717,12 +1722,12 @@ protected void configureCommitCache() { int daysToCache = settings.getInteger(Keys.web.activityCacheDays, 14); if (daysToCache <= 0) { logger.info("commit cache disabled"); logger.info("Commit cache is disabled"); } else { long start = System.nanoTime(); long repoCount = 0; long commitCount = 0; logger.info(MessageFormat.format("preparing {0} day commit cache. please wait...", daysToCache)); logger.info(MessageFormat.format("Preparing {0} day commit cache. please wait...", daysToCache)); CommitCache.instance().setCacheDays(daysToCache); Date cutoff = CommitCache.instance().getCutoffDate(); for (String repositoryName : getRepositoryList()) { src/main/java/com/gitblit/manager/RuntimeManager.java
@@ -40,23 +40,29 @@ private final ServerStatus serverStatus; private TimeZone timezone; private final ServerSettings settingsModel; private File baseFolder; private ServerSettings settingsModel; private TimeZone timezone; public RuntimeManager(IStoredSettings settings) { this(settings, null); } public RuntimeManager(IStoredSettings settings, File baseFolder) { this.settings = settings; this.settingsModel = new ServerSettings(); this.serverStatus = new ServerStatus(); this.baseFolder = baseFolder == null ? new File("") : baseFolder; } @Override public RuntimeManager setup() { logger.info("Gitblit settings = " + settings.toString()); logTimezone("JVM", TimeZone.getDefault()); logTimezone(Constants.NAME, getTimezone()); public RuntimeManager start() { logger.info("Basefolder : " + baseFolder.getAbsolutePath()); logger.info("Settings : " + settings.toString()); logTimezone("JVM timezone: ", TimeZone.getDefault()); logTimezone("App timezone: ", getTimezone()); return this; } @@ -121,7 +127,7 @@ @Override public TimeZone getTimezone() { if (timezone == null) { String tzid = settings.getString("web.timezone", null); String tzid = settings.getString(Keys.web.timezone, null); if (StringUtils.isEmpty(tzid)) { timezone = TimeZone.getDefault(); return timezone; @@ -135,7 +141,7 @@ SimpleDateFormat df = new SimpleDateFormat("z Z"); df.setTimeZone(zone); String offset = df.format(new Date()); logger.info(type + " timezone is " + zone.getID() + " (" + offset + ")"); logger.info("{}{} ({})", new Object [] { type, zone.getID(), offset }); } /** src/main/java/com/gitblit/manager/ServicesManager.java
New file @@ -0,0 +1,188 @@ /* * 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.IOException; import java.text.MessageFormat; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gitblit.Constants.FederationToken; import com.gitblit.FederationPullExecutor; import com.gitblit.Gitblit; import com.gitblit.IStoredSettings; import com.gitblit.Keys; import com.gitblit.fanout.FanoutNioService; import com.gitblit.fanout.FanoutService; import com.gitblit.fanout.FanoutSocketService; import com.gitblit.git.GitDaemon; import com.gitblit.models.FederationModel; import com.gitblit.utils.StringUtils; import com.gitblit.utils.TimeUtils; /** * Services manager manages long-running services/processes that either have no * direct relation to other managers OR require really high-level manager * integration (i.e. a Gitblit instance). * * @author James Moger * */ public class ServicesManager implements IServicesManager { private final Logger logger = LoggerFactory.getLogger(getClass()); private final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(5); private final IStoredSettings settings; private final Gitblit gitblit; private FanoutService fanoutService; private GitDaemon gitDaemon; public ServicesManager(Gitblit gitblit) { this.settings = gitblit.getSettings(); this.gitblit = gitblit; } @Override public ServicesManager start() { configureFederation(); configureFanout(); configureGitDaemon(); return this; } @Override public ServicesManager stop() { scheduledExecutor.shutdownNow(); if (fanoutService != null) { fanoutService.stop(); } if (gitDaemon != null) { gitDaemon.stop(); } return this; } protected void configureFederation() { boolean validPassphrase = true; String passphrase = settings.getString(Keys.federation.passphrase, ""); if (StringUtils.isEmpty(passphrase)) { logger.info("Federation passphrase is blank! This server can not be PULLED from."); validPassphrase = false; } if (validPassphrase) { // standard tokens for (FederationToken tokenType : FederationToken.values()) { logger.info(MessageFormat.format("Federation {0} token = {1}", tokenType.name(), gitblit.getFederationToken(tokenType))); } // federation set tokens for (String set : settings.getStrings(Keys.federation.sets)) { logger.info(MessageFormat.format("Federation Set {0} token = {1}", set, gitblit.getFederationToken(set))); } } // Schedule or run the federation executor List<FederationModel> registrations = gitblit.getFederationRegistrations(); if (registrations.size() > 0) { FederationPuller executor = new FederationPuller(registrations); scheduledExecutor.schedule(executor, 1, TimeUnit.MINUTES); } } protected void configureGitDaemon() { int port = settings.getInteger(Keys.git.daemonPort, 0); String bindInterface = settings.getString(Keys.git.daemonBindInterface, "localhost"); if (port > 0) { try { gitDaemon = new GitDaemon(gitblit); gitDaemon.start(); } catch (IOException e) { gitDaemon = null; logger.error(MessageFormat.format("Failed to start Git Daemon on {0}:{1,number,0}", bindInterface, port), e); } } else { logger.info("Git Daemon is disabled."); } } protected void configureFanout() { // startup Fanout PubSub service if (settings.getInteger(Keys.fanout.port, 0) > 0) { String bindInterface = settings.getString(Keys.fanout.bindInterface, null); int port = settings.getInteger(Keys.fanout.port, FanoutService.DEFAULT_PORT); boolean useNio = settings.getBoolean(Keys.fanout.useNio, true); int limit = settings.getInteger(Keys.fanout.connectionLimit, 0); if (useNio) { if (StringUtils.isEmpty(bindInterface)) { fanoutService = new FanoutNioService(port); } else { fanoutService = new FanoutNioService(bindInterface, port); } } else { if (StringUtils.isEmpty(bindInterface)) { fanoutService = new FanoutSocketService(port); } else { fanoutService = new FanoutSocketService(bindInterface, port); } } fanoutService.setConcurrentConnectionLimit(limit); fanoutService.setAllowAllChannelAnnouncements(false); fanoutService.start(); } else { logger.info("Fanout PubSub service is disabled."); } } private class FederationPuller extends FederationPullExecutor { public FederationPuller(FederationModel registration) { super(Arrays.asList(registration)); } public FederationPuller(List<FederationModel> registrations) { super(registrations); } @Override public void reschedule(FederationModel registration) { // schedule the next pull int mins = TimeUtils.convertFrequencyToMinutes(registration.frequency); registration.nextPull = new Date(System.currentTimeMillis() + (mins * 60 * 1000L)); scheduledExecutor.schedule(new FederationPuller(registration), mins, TimeUnit.MINUTES); logger.info(MessageFormat.format( "Next pull of {0} @ {1} scheduled for {2,date,yyyy-MM-dd HH:mm}", registration.name, registration.url, registration.nextPull)); } } } src/main/java/com/gitblit/manager/SessionManager.java
@@ -65,7 +65,7 @@ } @Override public IManager setup() { public SessionManager start() { List<String> services = settings.getStrings("realm.authenticationServices"); for (String service : services) { // TODO populate authentication services here @@ -74,7 +74,7 @@ } @Override public IManager stop() { public SessionManager stop() { return this; } src/main/java/com/gitblit/manager/UserManager.java
@@ -64,13 +64,13 @@ * @param userService */ public void setUserService(IUserService userService) { logger.info("Setting up user service " + userService.toString()); logger.info("UserService: " + userService.toString()); this.userService = userService; this.userService.setup(runtimeManager); } @Override public IManager setup() { public UserManager start() { if (this.userService == null) { String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.properties"); IUserService service = null; @@ -114,7 +114,7 @@ } @Override public IManager stop() { public UserManager stop() { return this; } src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java
@@ -137,7 +137,7 @@ } @Override public IRuntimeManager setup() { public IRuntimeManager start() { return this; } }