| /* |
| * 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; |
| |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| 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; |
| import com.gitblit.manager.IUserManager; |
| import com.gitblit.manager.ServicesManager; |
| 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 |
| * management functions and also manages some long-running services. |
| * |
| * @author James Moger |
| * |
| */ |
| 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); |
| } |
| |
| @Override |
| public GitBlit start() { |
| super.start(); |
| logger.info("Starting services manager..."); |
| servicesManager.start(); |
| configureTicketService(); |
| return this; |
| } |
| |
| @Override |
| public GitBlit stop() { |
| super.stop(); |
| servicesManager.stop(); |
| ticketService.stop(); |
| return this; |
| } |
| |
| @Override |
| public boolean isServingRepositories() { |
| return servicesManager.isServingRepositories(); |
| } |
| |
| @Override |
| public boolean isServingHTTP() { |
| return servicesManager.isServingHTTP(); |
| } |
| |
| @Override |
| public boolean isServingGIT() { |
| return servicesManager.isServingGIT(); |
| } |
| |
| @Override |
| public boolean isServingSSH() { |
| return servicesManager.isServingSSH(); |
| } |
| |
| 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); |
| } |
| |
| /** |
| * 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)) { |
| 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)); |
| } |
| } |
| |
| // git daemon url |
| String gitDaemonUrl = servicesManager.getGitDaemonUrl(request, user, repository); |
| 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)); |
| } |
| } |
| |
| // 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)); |
| } |
| } |
| |
| // 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); |
| } |
| }); |
| |
| // consider the user's transport preference |
| RepositoryUrl preferredUrl = null; |
| Transport preferredTransport = user.getPreferences().getTransport(); |
| if (preferredTransport != null) { |
| Iterator<RepositoryUrl> itr = list.iterator(); |
| while (itr.hasNext()) { |
| RepositoryUrl url = itr.next(); |
| if (url.transport.equals(preferredTransport)) { |
| itr.remove(); |
| preferredUrl = url; |
| break; |
| } |
| } |
| } |
| if (preferredUrl != null) { |
| list.add(0, preferredUrl); |
| } |
| |
| 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); |
| } |
| } |
| } |