From 7baf2e4cd2ef8082b74937e26de75b01e630b5d4 Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Thu, 17 Apr 2014 12:00:35 -0400 Subject: [PATCH] Expose JGit 3.x receive pack settings as Gitblit settings --- src/main/java/com/gitblit/GitBlit.java | 317 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 317 insertions(+), 0 deletions(-) diff --git a/src/main/java/com/gitblit/GitBlit.java b/src/main/java/com/gitblit/GitBlit.java index da80a74..3db5f08 100644 --- a/src/main/java/com/gitblit/GitBlit.java +++ b/src/main/java/com/gitblit/GitBlit.java @@ -17,15 +17,23 @@ import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import javax.inject.Singleton; import javax.servlet.http.HttpServletRequest; import com.gitblit.Constants.AccessPermission; +import com.gitblit.Constants.Transport; import com.gitblit.manager.GitblitManager; import com.gitblit.manager.IAuthenticationManager; import com.gitblit.manager.IFederationManager; +import com.gitblit.manager.IGitblit; import com.gitblit.manager.INotificationManager; +import com.gitblit.manager.IPluginManager; import com.gitblit.manager.IProjectManager; import com.gitblit.manager.IRepositoryManager; import com.gitblit.manager.IRuntimeManager; @@ -34,7 +42,17 @@ import com.gitblit.models.RepositoryModel; import com.gitblit.models.RepositoryUrl; import com.gitblit.models.UserModel; +import com.gitblit.tickets.BranchTicketService; +import com.gitblit.tickets.FileTicketService; +import com.gitblit.tickets.ITicketService; +import com.gitblit.tickets.NullTicketService; +import com.gitblit.tickets.RedisTicketService; +import com.gitblit.transport.ssh.IPublicKeyManager; import com.gitblit.utils.StringUtils; + +import dagger.Module; +import dagger.ObjectGraph; +import dagger.Provides; /** * GitBlit is the aggregate manager for the Gitblit webapp. It provides all @@ -45,24 +63,34 @@ */ public class GitBlit extends GitblitManager { + private final ObjectGraph injector; + private final ServicesManager servicesManager; + + private ITicketService ticketService; public GitBlit( IRuntimeManager runtimeManager, + IPluginManager pluginManager, INotificationManager notificationManager, IUserManager userManager, IAuthenticationManager authenticationManager, + IPublicKeyManager publicKeyManager, IRepositoryManager repositoryManager, IProjectManager projectManager, IFederationManager federationManager) { super(runtimeManager, + pluginManager, notificationManager, userManager, authenticationManager, + publicKeyManager, repositoryManager, projectManager, federationManager); + + this.injector = ObjectGraph.create(getModules()); this.servicesManager = new ServicesManager(this); } @@ -72,6 +100,7 @@ super.start(); logger.info("Starting services manager..."); servicesManager.start(); + configureTicketService(); return this; } @@ -79,7 +108,43 @@ public GitBlit stop() { super.stop(); servicesManager.stop(); + ticketService.stop(); return this; + } + + @Override + public boolean isServingRepositories() { + return servicesManager.isServingRepositories(); + } + + protected Object [] getModules() { + return new Object [] { new GitBlitModule()}; + } + + protected boolean acceptPush(Transport byTransport) { + if (byTransport == null) { + logger.info("Unknown transport, push rejected!"); + return false; + } + + Set<Transport> transports = new HashSet<Transport>(); + for (String value : getSettings().getStrings(Keys.git.acceptedPushTransports)) { + Transport transport = Transport.fromString(value); + if (transport == null) { + logger.info(String.format("Ignoring unknown registered transport %s", value)); + continue; + } + + transports.add(transport); + } + + if (transports.isEmpty()) { + // no transports are explicitly specified, all are acceptable + return true; + } + + // verify that the transport is permitted + return transports.contains(byTransport); } /** @@ -98,11 +163,33 @@ 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)) { + Transport transport = Transport.fromString(request.getScheme()); + if (permission.atLeast(AccessPermission.PUSH) && !acceptPush(transport)) { + // downgrade the repo permission for this transport + // because it is not an acceptable PUSH transport + permission = AccessPermission.CLONE; + } list.add(new RepositoryUrl(getRepositoryUrl(request, username, repository), permission)); + } + } + + // ssh daemon url + String sshDaemonUrl = servicesManager.getSshDaemonUrl(request, user, repository); + if (!StringUtils.isEmpty(sshDaemonUrl)) { + AccessPermission permission = user.getRepositoryPermission(repository).permission; + if (permission.exceeds(AccessPermission.NONE)) { + if (permission.atLeast(AccessPermission.PUSH) && !acceptPush(Transport.SSH)) { + // downgrade the repo permission for this transport + // because it is not an acceptable PUSH transport + permission = AccessPermission.CLONE; + } + + list.add(new RepositoryUrl(sshDaemonUrl, permission)); } } @@ -111,6 +198,11 @@ if (!StringUtils.isEmpty(gitDaemonUrl)) { AccessPermission permission = servicesManager.getGitDaemonAccessPermission(user, repository); if (permission.exceeds(AccessPermission.NONE)) { + if (permission.atLeast(AccessPermission.PUSH) && !acceptPush(Transport.GIT)) { + // downgrade the repo permission for this transport + // because it is not an acceptable PUSH transport + permission = AccessPermission.CLONE; + } list.add(new RepositoryUrl(gitDaemonUrl, permission)); } } @@ -129,6 +221,231 @@ list.add(new RepositoryUrl(MessageFormat.format(url, repository.name), null)); } } + + // sort transports by highest permission and then by transport security + Collections.sort(list, new Comparator<RepositoryUrl>() { + + @Override + public int compare(RepositoryUrl o1, RepositoryUrl o2) { + if (!o1.isExternal() && o2.isExternal()) { + // prefer Gitblit over external + return -1; + } else if (o1.isExternal() && !o2.isExternal()) { + // prefer Gitblit over external + return 1; + } else if (o1.isExternal() && o2.isExternal()) { + // sort by Transport ordinal + return o1.transport.compareTo(o2.transport); + } else if (o1.permission.exceeds(o2.permission)) { + // prefer highest permission + return -1; + } else if (o2.permission.exceeds(o1.permission)) { + // prefer highest permission + return 1; + } + + // prefer more secure transports + return o1.transport.compareTo(o2.transport); + } + }); + return list; } + + /** + * Detect renames and reindex as appropriate. + */ + @Override + public void updateRepositoryModel(String repositoryName, RepositoryModel repository, + boolean isCreate) throws GitBlitException { + RepositoryModel oldModel = null; + boolean isRename = !isCreate && !repositoryName.equalsIgnoreCase(repository.name); + if (isRename) { + oldModel = repositoryManager.getRepositoryModel(repositoryName); + } + + super.updateRepositoryModel(repositoryName, repository, isCreate); + + if (isRename && ticketService != null) { + ticketService.rename(oldModel, repository); + } + } + + /** + * Delete the user and all associated public ssh keys. + */ + @Override + public boolean deleteUser(String username) { + UserModel user = userManager.getUserModel(username); + return deleteUserModel(user); + } + + @Override + public boolean deleteUserModel(UserModel model) { + boolean success = userManager.deleteUserModel(model); + if (success) { + getPublicKeyManager().removeAllKeys(model.username); + } + return success; + } + + /** + * Delete the repository and all associated tickets. + */ + @Override + public boolean deleteRepository(String repositoryName) { + RepositoryModel repository = repositoryManager.getRepositoryModel(repositoryName); + return deleteRepositoryModel(repository); + } + + @Override + public boolean deleteRepositoryModel(RepositoryModel model) { + boolean success = repositoryManager.deleteRepositoryModel(model); + if (success && ticketService != null) { + ticketService.deleteAll(model); + } + return success; + } + + /** + * Returns the configured ticket service. + * + * @return a ticket service + */ + @Override + public ITicketService getTicketService() { + return ticketService; + } + + protected void configureTicketService() { + String clazz = settings.getString(Keys.tickets.service, NullTicketService.class.getName()); + if (StringUtils.isEmpty(clazz)) { + clazz = NullTicketService.class.getName(); + } + try { + Class<? extends ITicketService> serviceClass = (Class<? extends ITicketService>) Class.forName(clazz); + ticketService = injector.get(serviceClass).start(); + if (ticketService instanceof NullTicketService) { + logger.warn("No ticket service configured."); + } else if (ticketService.isReady()) { + logger.info("{} is ready.", ticketService); + } else { + logger.warn("{} is disabled.", ticketService); + } + } catch (Exception e) { + logger.error("failed to create ticket service " + clazz, e); + ticketService = injector.get(NullTicketService.class).start(); + } + } + + /** + * A nested Dagger graph is used for constructor dependency injection of + * complex classes. + * + * @author James Moger + * + */ + @Module( + library = true, + injects = { + IStoredSettings.class, + + // core managers + IRuntimeManager.class, + IPluginManager.class, + INotificationManager.class, + IUserManager.class, + IAuthenticationManager.class, + IRepositoryManager.class, + IProjectManager.class, + IFederationManager.class, + + // the monolithic manager + IGitblit.class, + + // ticket services + NullTicketService.class, + FileTicketService.class, + BranchTicketService.class, + RedisTicketService.class + } + ) + class GitBlitModule { + + @Provides @Singleton IStoredSettings provideSettings() { + return settings; + } + + @Provides @Singleton IRuntimeManager provideRuntimeManager() { + return runtimeManager; + } + + @Provides @Singleton IPluginManager providePluginManager() { + return pluginManager; + } + + @Provides @Singleton INotificationManager provideNotificationManager() { + return notificationManager; + } + + @Provides @Singleton IUserManager provideUserManager() { + return userManager; + } + + @Provides @Singleton IAuthenticationManager provideAuthenticationManager() { + return authenticationManager; + } + + @Provides @Singleton IRepositoryManager provideRepositoryManager() { + return repositoryManager; + } + + @Provides @Singleton IProjectManager provideProjectManager() { + return projectManager; + } + + @Provides @Singleton IFederationManager provideFederationManager() { + return federationManager; + } + + @Provides @Singleton IGitblit provideGitblit() { + return GitBlit.this; + } + + @Provides @Singleton NullTicketService provideNullTicketService() { + return new NullTicketService( + runtimeManager, + pluginManager, + notificationManager, + userManager, + repositoryManager); + } + + @Provides @Singleton FileTicketService provideFileTicketService() { + return new FileTicketService( + runtimeManager, + pluginManager, + notificationManager, + userManager, + repositoryManager); + } + + @Provides @Singleton BranchTicketService provideBranchTicketService() { + return new BranchTicketService( + runtimeManager, + pluginManager, + notificationManager, + userManager, + repositoryManager); + } + + @Provides @Singleton RedisTicketService provideRedisTicketService() { + return new RedisTicketService( + runtimeManager, + pluginManager, + notificationManager, + userManager, + repositoryManager); + } + } } -- Gitblit v1.9.1