| | |
| | | import java.lang.reflect.Type; |
| | | import java.net.URI; |
| | | import java.net.URISyntaxException; |
| | | import java.nio.charset.Charset; |
| | | import java.security.Principal; |
| | | import java.text.MessageFormat; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.ArrayList; |
| | |
| | | import java.util.Map; |
| | | import java.util.Map.Entry; |
| | | import java.util.Set; |
| | | import java.util.TimeZone; |
| | | import java.util.TreeMap; |
| | | import java.util.TreeSet; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | import java.util.concurrent.atomic.AtomicReference; |
| | | |
| | | import javax.mail.Message; |
| | | import javax.mail.MessagingException; |
| | | import javax.mail.internet.MimeBodyPart; |
| | | import javax.mail.internet.MimeMultipart; |
| | | 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.Cookie; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | |
| | | import org.apache.wicket.RequestCycle; |
| | | import org.apache.wicket.resource.ContextRelativeResource; |
| | | import org.apache.wicket.util.resource.ResourceStreamNotFoundException; |
| | | import org.eclipse.jgit.lib.Repository; |
| | |
| | | |
| | | import com.gitblit.Constants.AccessPermission; |
| | | import com.gitblit.Constants.AccessRestrictionType; |
| | | import com.gitblit.Constants.AccountType; |
| | | import com.gitblit.Constants.AuthenticationType; |
| | | import com.gitblit.Constants.AuthorizationControl; |
| | | import com.gitblit.Constants.CommitMessageRenderer; |
| | | import com.gitblit.Constants.FederationRequest; |
| | |
| | | import com.gitblit.git.GitServlet; |
| | | 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; |
| | |
| | | import com.gitblit.models.RepositoryUrl; |
| | | import com.gitblit.models.SearchResult; |
| | | import com.gitblit.models.ServerSettings; |
| | | import com.gitblit.models.ServerStatus; |
| | | import com.gitblit.models.SettingModel; |
| | | import com.gitblit.models.TeamModel; |
| | | import com.gitblit.models.UserModel; |
| | | import com.gitblit.utils.ArrayUtils; |
| | | import com.gitblit.utils.Base64; |
| | | import com.gitblit.utils.ByteFormat; |
| | | import com.gitblit.utils.CommitCache; |
| | | import com.gitblit.utils.ContainerUtils; |
| | |
| | | import com.gitblit.utils.ObjectCache; |
| | | import com.gitblit.utils.StringUtils; |
| | | import com.gitblit.utils.TimeUtils; |
| | | import com.gitblit.utils.X509Utils.X509Metadata; |
| | | import com.gitblit.wicket.GitBlitWebSession; |
| | | 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 |
| | |
| | | */ |
| | | @WebListener |
| | | public class GitBlit extends DaggerContextListener |
| | | implements IRuntimeManager, |
| | | INotificationManager, |
| | | IUserManager, |
| | | ISessionManager, |
| | | IRepositoryManager, |
| | | implements IRepositoryManager, |
| | | IProjectManager, |
| | | IFederationManager, |
| | | IGitblitManager { |
| | |
| | | private static GitBlit gitblit; |
| | | |
| | | 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 ObjectCache<String> projectRepositoriesMarkdownCache = new ObjectCache<String>(); |
| | | |
| | | private File baseFolder; |
| | | |
| | | private File repositoriesFolder; |
| | | |
| | | private IUserService userService; |
| | | |
| | | private IStoredSettings settings; |
| | | |
| | | private ServerSettings settingsModel; |
| | | |
| | | private ServerStatus serverStatus; |
| | | |
| | | private MailExecutor mailExecutor; |
| | | |
| | | private LuceneExecutor luceneExecutor; |
| | | |
| | | private GCExecutor gcExecutor; |
| | | |
| | | private MirrorExecutor mirrorExecutor; |
| | | |
| | | private TimeZone timezone; |
| | | |
| | | private FileBasedConfig projectConfigs; |
| | | |
| | |
| | | |
| | | public GitBlit() { |
| | | this.goSettings = null; |
| | | } |
| | | |
| | | protected GitBlit(final IUserService userService) { |
| | | this.goSettings = null; |
| | | this.userService = userService; |
| | | gitblit = this; |
| | | this.goBaseFolder = null; |
| | | } |
| | | |
| | | public GitBlit(IStoredSettings settings, File baseFolder) { |
| | | this.goSettings = settings; |
| | | this.baseFolder = baseFolder; |
| | | this.goBaseFolder = baseFolder; |
| | | gitblit = this; |
| | | } |
| | | |
| | |
| | | if (managerClass.isAssignableFrom(GitBlit.class)) { |
| | | return (X) gitblit; |
| | | } |
| | | |
| | | for (IManager manager : gitblit.managers) { |
| | | if (managerClass.isAssignableFrom(manager.getClass())) { |
| | | return (X) manager; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public File getBaseFolder() { |
| | | return baseFolder; |
| | | } |
| | | |
| | | @Override |
| | | public void setBaseFolder(File folder) { |
| | | this.baseFolder = folder; |
| | | } |
| | | |
| | | /** |
| | | * Returns the boot date of the Gitblit server. |
| | | * |
| | | * @return the boot date of Gitblit |
| | | */ |
| | | @Override |
| | | public Date getBootDate() { |
| | | return serverStatus.bootDate; |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * Determine if this Gitblit instance is actively serving git repositories |
| | | * or if it is merely a repository viewer. |
| | | * |
| | | * @return true if Gitblit is serving repositories |
| | | */ |
| | | @Override |
| | | public boolean isServingRepositories() { |
| | | return settings.getBoolean(Keys.git.enableGitServlet, true) || (settings.getInteger(Keys.git.daemonPort, 0) > 0); |
| | | } |
| | | |
| | | /** |
| | | * Returns the preferred timezone for the Gitblit instance. |
| | | * |
| | | * @return a timezone |
| | | */ |
| | | @Override |
| | | public TimeZone getTimezone() { |
| | | if (timezone == null) { |
| | | String tzid = settings.getString("web.timezone", null); |
| | | if (StringUtils.isEmpty(tzid)) { |
| | | timezone = TimeZone.getDefault(); |
| | | return timezone; |
| | | } |
| | | timezone = TimeZone.getTimeZone(tzid); |
| | | } |
| | | return timezone; |
| | | } |
| | | |
| | | /** |
| | | * Is Gitblit running in debug mode? |
| | | * |
| | | * @return true if Gitblit is running in debug mode |
| | | */ |
| | | @Override |
| | | public boolean isDebugMode() { |
| | | return settings.getBoolean(Keys.web.debugMode, false); |
| | | } |
| | | |
| | | /** |
| | | * Returns the file object for the specified configuration key. |
| | | * |
| | | * @return the file |
| | | */ |
| | | @Override |
| | | public File getFileOrFolder(String key, String defaultFileOrFolder) { |
| | | String fileOrFolder = settings.getString(key, defaultFileOrFolder); |
| | | return getFileOrFolder(fileOrFolder); |
| | | } |
| | | |
| | | /** |
| | | * Returns the file object which may have it's base-path determined by |
| | | * environment variables for running on a cloud hosting service. All Gitblit |
| | | * file or folder retrievals are (at least initially) funneled through this |
| | | * method so it is the correct point to globally override/alter filesystem |
| | | * access based on environment or some other indicator. |
| | | * |
| | | * @return the file |
| | | */ |
| | | @Override |
| | | public File getFileOrFolder(String fileOrFolder) { |
| | | return com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, |
| | | baseFolder, fileOrFolder); |
| | | } |
| | | |
| | | /** |
| | | * Returns the path of the repositories folder. This method checks to see if |
| | | * Gitblit is running on a cloud service and may return an adjusted path. |
| | | * |
| | |
| | | */ |
| | | @Override |
| | | public File getRepositoriesFolder() { |
| | | return getFileOrFolder(Keys.git.repositoriesFolder, "${baseFolder}/git"); |
| | | return getManager(IRuntimeManager.class).getFileOrFolder(Keys.git.repositoriesFolder, "${baseFolder}/git"); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Override |
| | | public File getProposalsFolder() { |
| | | return getFileOrFolder(Keys.federation.proposalsFolder, "${baseFolder}/proposals"); |
| | | return getManager(IRuntimeManager.class).getFileOrFolder(Keys.federation.proposalsFolder, "${baseFolder}/proposals"); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Override |
| | | public File getHooksFolder() { |
| | | return getFileOrFolder(Keys.groovy.scriptsFolder, "${baseFolder}/groovy"); |
| | | return getManager(IRuntimeManager.class).getFileOrFolder(Keys.groovy.scriptsFolder, "${baseFolder}/groovy"); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Override |
| | | public File getGrapesFolder() { |
| | | return getFileOrFolder(Keys.groovy.grapeFolder, "${baseFolder}/groovy/grape"); |
| | | } |
| | | |
| | | /** |
| | | * Returns the runtime settings. |
| | | * |
| | | * @return runtime settings |
| | | */ |
| | | @Override |
| | | public IStoredSettings getSettings() { |
| | | return settings; |
| | | } |
| | | |
| | | /** |
| | | * Updates the runtime settings. |
| | | * |
| | | * @param settings |
| | | * @return true if the update succeeded |
| | | */ |
| | | @Override |
| | | public boolean updateSettings(Map<String, String> updatedSettings) { |
| | | return settings.saveSettings(updatedSettings); |
| | | } |
| | | |
| | | @Override |
| | | public ServerStatus getStatus() { |
| | | // update heap memory status |
| | | serverStatus.heapAllocated = Runtime.getRuntime().totalMemory(); |
| | | serverStatus.heapFree = Runtime.getRuntime().freeMemory(); |
| | | return serverStatus; |
| | | return getManager(IRuntimeManager.class).getFileOrFolder(Keys.groovy.grapeFolder, "${baseFolder}/groovy/grape"); |
| | | } |
| | | |
| | | /** |
| | |
| | | if (user == null) { |
| | | user = UserModel.ANONYMOUS; |
| | | } |
| | | String username = encodeUsername(UserModel.ANONYMOUS.equals(user) ? "" : user.username); |
| | | String username = StringUtils.encodeUsername(UserModel.ANONYMOUS.equals(user) ? "" : user.username); |
| | | |
| | | List<RepositoryUrl> list = new ArrayList<RepositoryUrl>(); |
| | | // http/https url |
| | |
| | | @Override |
| | | public Collection<GitClientApplication> getClientApplications() { |
| | | // prefer user definitions, if they exist |
| | | File userDefs = new File(baseFolder, "clientapps.json"); |
| | | File userDefs = new File(getManager(IRuntimeManager.class).getBaseFolder(), "clientapps.json"); |
| | | if (userDefs.exists()) { |
| | | Date lastModified = new Date(userDefs.lastModified()); |
| | | if (clientApplications.hasCurrent("user", lastModified)) { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Set the user service. The user service authenticates all users and is |
| | | * responsible for managing user permissions. |
| | | * |
| | | * @param userService |
| | | */ |
| | | public void setUserService(IUserService userService) { |
| | | logger.info("Setting up user service " + userService.toString()); |
| | | this.userService = userService; |
| | | this.userService.setup(settings); |
| | | } |
| | | |
| | | @Override |
| | | public boolean supportsAddUser() { |
| | | return supportsCredentialChanges(new UserModel("")); |
| | | } |
| | | |
| | | /** |
| | | * Returns true if the user's credentials can be changed. |
| | | * |
| | | * @param user |
| | | * @return true if the user service supports credential changes |
| | | */ |
| | | @Override |
| | | public boolean supportsCredentialChanges(UserModel user) { |
| | | if (user == null) { |
| | | return false; |
| | | } else if (AccountType.LOCAL.equals(user.accountType)) { |
| | | // local account, we can change credentials |
| | | return true; |
| | | } else { |
| | | // external account, ask user service |
| | | return userService.supportsCredentialChanges(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Returns true if the user's display name can be changed. |
| | | * |
| | | * @param user |
| | | * @return true if the user service supports display name changes |
| | | */ |
| | | @Override |
| | | public boolean supportsDisplayNameChanges(UserModel user) { |
| | | return (user != null && user.isLocalAccount()) || userService.supportsDisplayNameChanges(); |
| | | } |
| | | |
| | | /** |
| | | * Returns true if the user's email address can be changed. |
| | | * |
| | | * @param user |
| | | * @return true if the user service supports email address changes |
| | | */ |
| | | @Override |
| | | public boolean supportsEmailAddressChanges(UserModel user) { |
| | | return (user != null && user.isLocalAccount()) || userService.supportsEmailAddressChanges(); |
| | | } |
| | | |
| | | /** |
| | | * Returns true if the user's team memberships can be changed. |
| | | * |
| | | * @param user |
| | | * @return true if the user service supports team membership changes |
| | | */ |
| | | @Override |
| | | public boolean supportsTeamMembershipChanges(UserModel user) { |
| | | return (user != null && user.isLocalAccount()) || userService.supportsTeamMembershipChanges(); |
| | | } |
| | | |
| | | /** |
| | | * Returns true if the username represents an internal account |
| | | * |
| | | * @param username |
| | | * @return true if the specified username represents an internal account |
| | | */ |
| | | protected boolean isInternalAccount(String username) { |
| | | return !StringUtils.isEmpty(username) |
| | | && (username.equalsIgnoreCase(Constants.FEDERATION_USER) |
| | | || username.equalsIgnoreCase(UserModel.ANONYMOUS.username)); |
| | | } |
| | | |
| | | /** |
| | | * Authenticate a user based on a username and password. |
| | | * |
| | | * @see IUserService.authenticate(String, char[]) |
| | | * @param username |
| | | * @param password |
| | | * @return a user object or null |
| | | */ |
| | | @Override |
| | | public UserModel authenticate(String username, char[] password) { |
| | | if (StringUtils.isEmpty(username)) { |
| | | // can not authenticate empty username |
| | | return null; |
| | | } |
| | | String usernameDecoded = decodeUsername(username); |
| | | String pw = new String(password); |
| | | if (StringUtils.isEmpty(pw)) { |
| | | // can not authenticate empty password |
| | | return null; |
| | | } |
| | | |
| | | // check to see if this is the federation user |
| | | if (canFederate()) { |
| | | if (usernameDecoded.equalsIgnoreCase(Constants.FEDERATION_USER)) { |
| | | List<String> tokens = getFederationTokens(); |
| | | if (tokens.contains(pw)) { |
| | | return getFederationUser(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // delegate authentication to the user service |
| | | if (userService == null) { |
| | | return null; |
| | | } |
| | | return userService.authenticate(usernameDecoded, password); |
| | | } |
| | | |
| | | /** |
| | | * Authenticate a user based on their cookie. |
| | | * |
| | | * @param cookies |
| | | * @return a user object or null |
| | | */ |
| | | protected UserModel authenticate(Cookie[] cookies) { |
| | | if (userService == null) { |
| | | return null; |
| | | } |
| | | if (userService.supportsCookies()) { |
| | | if (cookies != null && cookies.length > 0) { |
| | | for (Cookie cookie : cookies) { |
| | | if (cookie.getName().equals(Constants.NAME)) { |
| | | String value = cookie.getValue(); |
| | | return userService.authenticate(value.toCharArray()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * Authenticate a user based on HTTP request parameters. |
| | | * |
| | | * Authentication by X509Certificate is tried first and then by cookie. |
| | | * |
| | | * @param httpRequest |
| | | * @return a user object or null |
| | | */ |
| | | @Override |
| | | public UserModel authenticate(HttpServletRequest httpRequest) { |
| | | return authenticate(httpRequest, false); |
| | | } |
| | | |
| | | /** |
| | | * Authenticate a user based on HTTP request parameters. |
| | | * |
| | | * Authentication by X509Certificate, servlet container principal, cookie, |
| | | * and BASIC header. |
| | | * |
| | | * @param httpRequest |
| | | * @param requiresCertificate |
| | | * @return a user object or null |
| | | */ |
| | | @Override |
| | | public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) { |
| | | // try to authenticate by certificate |
| | | boolean checkValidity = settings.getBoolean(Keys.git.enforceCertificateValidity, true); |
| | | String [] oids = settings.getStrings(Keys.git.certificateUsernameOIDs).toArray(new String[0]); |
| | | UserModel model = HttpUtils.getUserModelFromCertificate(httpRequest, checkValidity, oids); |
| | | if (model != null) { |
| | | // grab real user model and preserve certificate serial number |
| | | UserModel user = getUserModel(model.username); |
| | | X509Metadata metadata = HttpUtils.getCertificateMetadata(httpRequest); |
| | | if (user != null) { |
| | | flagWicketSession(AuthenticationType.CERTIFICATE); |
| | | logger.debug(MessageFormat.format("{0} authenticated by client certificate {1} from {2}", |
| | | user.username, metadata.serialNumber, httpRequest.getRemoteAddr())); |
| | | return user; |
| | | } else { |
| | | logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted client certificate ({1}) authentication from {2}", |
| | | model.username, metadata.serialNumber, httpRequest.getRemoteAddr())); |
| | | } |
| | | } |
| | | |
| | | if (requiresCertificate) { |
| | | // caller requires client certificate authentication (e.g. git servlet) |
| | | return null; |
| | | } |
| | | |
| | | // try to authenticate by servlet container principal |
| | | Principal principal = httpRequest.getUserPrincipal(); |
| | | if (principal != null) { |
| | | String username = principal.getName(); |
| | | if (!StringUtils.isEmpty(username)) { |
| | | boolean internalAccount = isInternalAccount(username); |
| | | UserModel user = getUserModel(username); |
| | | if (user != null) { |
| | | // existing user |
| | | flagWicketSession(AuthenticationType.CONTAINER); |
| | | logger.debug(MessageFormat.format("{0} authenticated by servlet container principal from {1}", |
| | | user.username, httpRequest.getRemoteAddr())); |
| | | return user; |
| | | } else if (settings.getBoolean(Keys.realm.container.autoCreateAccounts, false) |
| | | && !internalAccount) { |
| | | // auto-create user from an authenticated container principal |
| | | user = new UserModel(username.toLowerCase()); |
| | | user.displayName = username; |
| | | user.password = Constants.EXTERNAL_ACCOUNT; |
| | | userService.updateUserModel(user); |
| | | flagWicketSession(AuthenticationType.CONTAINER); |
| | | logger.debug(MessageFormat.format("{0} authenticated and created by servlet container principal from {1}", |
| | | user.username, httpRequest.getRemoteAddr())); |
| | | return user; |
| | | } else if (!internalAccount) { |
| | | logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted servlet container authentication from {1}", |
| | | principal.getName(), httpRequest.getRemoteAddr())); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // try to authenticate by cookie |
| | | if (supportsCookies()) { |
| | | UserModel user = authenticate(httpRequest.getCookies()); |
| | | if (user != null) { |
| | | flagWicketSession(AuthenticationType.COOKIE); |
| | | logger.debug(MessageFormat.format("{0} authenticated by cookie from {1}", |
| | | user.username, httpRequest.getRemoteAddr())); |
| | | return user; |
| | | } |
| | | } |
| | | |
| | | // try to authenticate by BASIC |
| | | final String authorization = httpRequest.getHeader("Authorization"); |
| | | if (authorization != null && authorization.startsWith("Basic")) { |
| | | // Authorization: Basic base64credentials |
| | | String base64Credentials = authorization.substring("Basic".length()).trim(); |
| | | String credentials = new String(Base64.decode(base64Credentials), |
| | | Charset.forName("UTF-8")); |
| | | // credentials = username:password |
| | | final String[] values = credentials.split(":",2); |
| | | |
| | | if (values.length == 2) { |
| | | String username = values[0]; |
| | | char[] password = values[1].toCharArray(); |
| | | UserModel user = authenticate(username, password); |
| | | if (user != null) { |
| | | flagWicketSession(AuthenticationType.CREDENTIALS); |
| | | logger.debug(MessageFormat.format("{0} authenticated by BASIC request header from {1}", |
| | | user.username, httpRequest.getRemoteAddr())); |
| | | return user; |
| | | } else { |
| | | logger.warn(MessageFormat.format("Failed login attempt for {0}, invalid credentials from {1}", |
| | | username, httpRequest.getRemoteAddr())); |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | protected void flagWicketSession(AuthenticationType authenticationType) { |
| | | RequestCycle requestCycle = RequestCycle.get(); |
| | | if (requestCycle != null) { |
| | | // flag the Wicket session, if this is a Wicket request |
| | | GitBlitWebSession session = GitBlitWebSession.get(); |
| | | session.authenticationType = authenticationType; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Open a file resource using the Servlet container. |
| | | * @param file to open |
| | | * @return InputStream of the opened file |
| | |
| | | return res.getResourceStream().getInputStream(); |
| | | } |
| | | |
| | | /** |
| | | * Sets a cookie for the specified user. |
| | | * |
| | | * @param response |
| | | * @param user |
| | | */ |
| | | @Override |
| | | public void setCookie(HttpServletResponse response, UserModel user) { |
| | | if (userService == null) { |
| | | return; |
| | | } |
| | | GitBlitWebSession session = GitBlitWebSession.get(); |
| | | boolean standardLogin = session.authenticationType.isStandard(); |
| | | |
| | | if (userService.supportsCookies() && standardLogin) { |
| | | Cookie userCookie; |
| | | if (user == null) { |
| | | // clear cookie for logout |
| | | userCookie = new Cookie(Constants.NAME, ""); |
| | | } else { |
| | | // set cookie for login |
| | | String cookie = userService.getCookie(user); |
| | | if (StringUtils.isEmpty(cookie)) { |
| | | // create empty cookie |
| | | userCookie = new Cookie(Constants.NAME, ""); |
| | | } else { |
| | | // create real cookie |
| | | userCookie = new Cookie(Constants.NAME, cookie); |
| | | userCookie.setMaxAge(Integer.MAX_VALUE); |
| | | } |
| | | } |
| | | userCookie.setPath("/"); |
| | | response.addCookie(userCookie); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Logout a user. |
| | | * |
| | | * @param user |
| | | */ |
| | | @Override |
| | | public void logout(UserModel user) { |
| | | if (userService == null) { |
| | | return; |
| | | } |
| | | userService.logout(user); |
| | | } |
| | | |
| | | /** |
| | | * Encode the username for user in an url. |
| | | * |
| | | * @param name |
| | | * @return the encoded name |
| | | */ |
| | | protected String encodeUsername(String name) { |
| | | return name.replace("@", "%40").replace(" ", "%20").replace("\\", "%5C"); |
| | | } |
| | | |
| | | /** |
| | | * Decode a username from an encoded url. |
| | | * |
| | | * @param name |
| | | * @return the decoded name |
| | | */ |
| | | protected String decodeUsername(String name) { |
| | | return name.replace("%40", "@").replace("%20", " ").replace("%5C", "\\"); |
| | | } |
| | | |
| | | /** |
| | | * Returns the list of all users available to the login service. |
| | | * |
| | | * @see IUserService.getAllUsernames() |
| | | * @return list of all usernames |
| | | */ |
| | | @Override |
| | | public List<String> getAllUsernames() { |
| | | List<String> names = new ArrayList<String>(userService.getAllUsernames()); |
| | | return names; |
| | | } |
| | | |
| | | /** |
| | | * Returns the list of all users available to the login service. |
| | | * |
| | | * @see IUserService.getAllUsernames() |
| | | * @return list of all usernames |
| | | */ |
| | | @Override |
| | | public List<UserModel> getAllUsers() { |
| | | List<UserModel> users = userService.getAllUsers(); |
| | | return users; |
| | | } |
| | | |
| | | /** |
| | | * Delete the user object with the specified username |
| | | * |
| | | * @see IUserService.deleteUser(String) |
| | | * @param username |
| | | * @return true if successful |
| | | */ |
| | | @Override |
| | | public boolean deleteUser(String username) { |
| | | if (StringUtils.isEmpty(username)) { |
| | | return false; |
| | | } |
| | | String usernameDecoded = decodeUsername(username); |
| | | return userService.deleteUser(usernameDecoded); |
| | | } |
| | | |
| | | @Override |
| | | public UserModel getFederationUser() { |
| | | // the federation user is an administrator |
| | | UserModel federationUser = new UserModel(Constants.FEDERATION_USER); |
| | | federationUser.canAdmin = true; |
| | | return federationUser; |
| | | } |
| | | |
| | | /** |
| | | * Retrieve the user object for the specified username. |
| | | * |
| | | * @see IUserService.getUserModel(String) |
| | | * @param username |
| | | * @return a user object or null |
| | | */ |
| | | @Override |
| | | public UserModel getUserModel(String username) { |
| | | if (StringUtils.isEmpty(username)) { |
| | | return null; |
| | | } |
| | | String usernameDecoded = decodeUsername(username); |
| | | UserModel user = userService.getUserModel(usernameDecoded); |
| | | return user; |
| | | } |
| | | |
| | | /** |
| | |
| | | return list; |
| | | } |
| | | // NAMED users and teams |
| | | for (UserModel user : userService.getAllUsers()) { |
| | | for (UserModel user : getManager(IUserManager.class).getAllUsers()) { |
| | | RegistrantAccessPermission ap = user.getRepositoryPermission(repository); |
| | | if (ap.permission.exceeds(AccessPermission.NONE)) { |
| | | list.add(ap); |
| | |
| | | for (RegistrantAccessPermission up : permissions) { |
| | | if (up.mutable) { |
| | | // only set editable defined permissions |
| | | UserModel user = userService.getUserModel(up.registrant); |
| | | UserModel user = getManager(IUserManager.class).getUserModel(up.registrant); |
| | | user.setRepositoryPermission(repository.name, up.permission); |
| | | users.add(user); |
| | | } |
| | | } |
| | | return userService.updateUserModels(users); |
| | | return getManager(IUserManager.class).updateUserModels(users); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Override |
| | | public List<String> getRepositoryUsers(RepositoryModel repository) { |
| | | return userService.getUsernamesForRepositoryRole(repository.name); |
| | | return getManager(IUserManager.class).getUsernamesForRepositoryRole(repository.name); |
| | | } |
| | | |
| | | /** |
| | |
| | | public void updateUserModel(String username, UserModel user, boolean isCreate) |
| | | throws GitBlitException { |
| | | if (!username.equalsIgnoreCase(user.username)) { |
| | | if (userService.getUserModel(user.username) != null) { |
| | | if (getManager(IUserManager.class).getUserModel(user.username) != null) { |
| | | throw new GitBlitException(MessageFormat.format( |
| | | "Failed to rename ''{0}'' because ''{1}'' already exists.", username, |
| | | user.username)); |
| | |
| | | } |
| | | } |
| | | } |
| | | if (!userService.updateUserModel(username, user)) { |
| | | if (!getManager(IUserManager.class).updateUserModel(username, user)) { |
| | | throw new GitBlitException(isCreate ? "Failed to add user!" : "Failed to update user!"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Returns the list of available teams that a user or repository may be |
| | | * assigned to. |
| | | * |
| | | * @return the list of teams |
| | | */ |
| | | public List<String> getAllTeamnames() { |
| | | List<String> teams = new ArrayList<String>(userService.getAllTeamNames()); |
| | | return teams; |
| | | } |
| | | |
| | | /** |
| | | * Returns the list of available teams that a user or repository may be |
| | | * assigned to. |
| | | * |
| | | * @return the list of teams |
| | | */ |
| | | @Override |
| | | public List<TeamModel> getAllTeams() { |
| | | List<TeamModel> teams = userService.getAllTeams(); |
| | | return teams; |
| | | } |
| | | |
| | | /** |
| | | * Returns the TeamModel object for the specified name. |
| | | * |
| | | * @param teamname |
| | | * @return a TeamModel object or null |
| | | */ |
| | | @Override |
| | | public TeamModel getTeamModel(String teamname) { |
| | | return userService.getTeamModel(teamname); |
| | | } |
| | | |
| | | /** |
| | |
| | | @Override |
| | | public List<RegistrantAccessPermission> getTeamAccessPermissions(RepositoryModel repository) { |
| | | List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>(); |
| | | for (TeamModel team : userService.getAllTeams()) { |
| | | for (TeamModel team : getManager(IUserManager.class).getAllTeams()) { |
| | | RegistrantAccessPermission ap = team.getRepositoryPermission(repository); |
| | | if (ap.permission.exceeds(AccessPermission.NONE)) { |
| | | list.add(ap); |
| | |
| | | for (RegistrantAccessPermission tp : permissions) { |
| | | if (tp.mutable) { |
| | | // only set explicitly defined access permissions |
| | | TeamModel team = userService.getTeamModel(tp.registrant); |
| | | TeamModel team = getManager(IUserManager.class).getTeamModel(tp.registrant); |
| | | team.setRepositoryPermission(repository.name, tp.permission); |
| | | teams.add(team); |
| | | } |
| | | } |
| | | return userService.updateTeamModels(teams); |
| | | return getManager(IUserManager.class).updateTeamModels(teams); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @Override |
| | | public List<String> getRepositoryTeams(RepositoryModel repository) { |
| | | return userService.getTeamnamesForRepositoryRole(repository.name); |
| | | return getManager(IUserManager.class).getTeamNamesForRepositoryRole(repository.name); |
| | | } |
| | | |
| | | /** |
| | |
| | | public void updateTeamModel(String teamname, TeamModel team, boolean isCreate) |
| | | throws GitBlitException { |
| | | if (!teamname.equalsIgnoreCase(team.name)) { |
| | | if (userService.getTeamModel(team.name) != null) { |
| | | 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 (!userService.updateTeamModel(teamname, team)) { |
| | | if (!getManager(IUserManager.class).updateTeamModel(teamname, team)) { |
| | | throw new GitBlitException(isCreate ? "Failed to add team!" : "Failed to update team!"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Delete the team object with the specified teamname |
| | | * |
| | | * @see IUserService.deleteTeam(String) |
| | | * @param teamname |
| | | * @return true if successful |
| | | */ |
| | | @Override |
| | | public boolean deleteTeam(String teamname) { |
| | | return userService.deleteTeam(teamname); |
| | | } |
| | | |
| | | /** |
| | |
| | | @Override |
| | | public long getStarCount(RepositoryModel repository) { |
| | | long count = 0; |
| | | for (UserModel user : getAllUsers()) { |
| | | for (UserModel user : getManager(IUserManager.class).getAllUsers()) { |
| | | if (user.getPreferences().isStarredRepository(repository.name)) { |
| | | count++; |
| | | } |
| | |
| | | if (project == null) { |
| | | project = new ProjectModel(name); |
| | | if (ModelUtils.isPersonalRepository(name)) { |
| | | UserModel user = getUserModel(ModelUtils.getUserNameFromRepoPath(name)); |
| | | UserModel user = getManager(IUserManager.class).getUserModel(ModelUtils.getUserNameFromRepoPath(name)); |
| | | if (user != null) { |
| | | project.title = user.getDisplayName(); |
| | | project.description = "personal repositories"; |
| | |
| | | } |
| | | RepositoryModel model = new RepositoryModel(); |
| | | model.isBare = r.isBare(); |
| | | File basePath = getFileOrFolder(Keys.git.repositoriesFolder, "${baseFolder}/git"); |
| | | File basePath = getRepositoriesFolder(); |
| | | if (model.isBare) { |
| | | model.name = com.gitblit.utils.FileUtils.getRelativePath(basePath, r.getDirectory()); |
| | | } else { |
| | |
| | | if (repositoryMetricsCache.hasCurrent(model.name, model.lastChange)) { |
| | | return new ArrayList<Metric>(repositoryMetricsCache.getObject(model.name)); |
| | | } |
| | | List<Metric> metrics = MetricUtils.getDateMetrics(repository, null, true, null, getTimezone()); |
| | | List<Metric> metrics = MetricUtils.getDateMetrics(repository, null, true, null, getManager(IRuntimeManager.class).getTimezone()); |
| | | repositoryMetricsCache.updateObject(model.name, model.lastChange, metrics); |
| | | return new ArrayList<Metric>(metrics); |
| | | } |
| | |
| | | repository.name)); |
| | | } |
| | | // rename the roles |
| | | if (!userService.renameRepositoryRole(repositoryName, repository.name)) { |
| | | if (!getManager(IUserManager.class).renameRepositoryRole(repositoryName, repository.name)) { |
| | | throw new GitBlitException(MessageFormat.format( |
| | | "Failed to rename repository permissions ''{0}'' to ''{1}''.", |
| | | repositoryName, repository.name)); |
| | |
| | | File folder = new File(repositoriesFolder, repositoryName); |
| | | if (folder.exists() && folder.isDirectory()) { |
| | | FileUtils.delete(folder, FileUtils.RECURSIVE | FileUtils.RETRY); |
| | | if (userService.deleteRepositoryRole(repositoryName)) { |
| | | if (getManager(IUserManager.class).deleteRepositoryRole(repositoryName)) { |
| | | logger.info(MessageFormat.format("Repository \"{0}\" deleted", repositoryName)); |
| | | return true; |
| | | } |
| | |
| | | } |
| | | |
| | | // send an email, if possible |
| | | sendMailToAdministrators("Federation proposal from " + proposal.url, |
| | | getManager(INotificationManager.class).sendMailToAdministrators("Federation proposal from " + proposal.url, |
| | | "Please review the proposal @ " + gitblitUrl + "/proposal/" + proposal.token); |
| | | return true; |
| | | } |
| | |
| | | |
| | | // Team Scripts |
| | | if (repository != null) { |
| | | for (String teamname : userService.getTeamnamesForRepositoryRole(repository.name)) { |
| | | TeamModel team = userService.getTeamModel(teamname); |
| | | for (String teamname : getManager(IUserManager.class).getTeamNamesForRepositoryRole(repository.name)) { |
| | | TeamModel team = getManager(IUserManager.class).getTeamModel(teamname); |
| | | if (!ArrayUtils.isEmpty(team.preReceiveScripts)) { |
| | | scripts.addAll(team.preReceiveScripts); |
| | | } |
| | |
| | | } |
| | | // Team Scripts |
| | | if (repository != null) { |
| | | for (String teamname : userService.getTeamnamesForRepositoryRole(repository.name)) { |
| | | TeamModel team = userService.getTeamModel(teamname); |
| | | for (String teamname : getManager(IUserManager.class).getTeamNamesForRepositoryRole(repository.name)) { |
| | | TeamModel team = getManager(IUserManager.class).getTeamModel(teamname); |
| | | if (!ArrayUtils.isEmpty(team.postReceiveScripts)) { |
| | | scripts.addAll(team.postReceiveScripts); |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * Notify the administrators by email. |
| | | * |
| | | * @param subject |
| | | * @param message |
| | | */ |
| | | @Override |
| | | public void sendMailToAdministrators(String subject, String message) { |
| | | List<String> toAddresses = settings.getStrings(Keys.mail.adminAddresses); |
| | | sendMail(subject, message, toAddresses); |
| | | } |
| | | |
| | | /** |
| | | * Notify users by email of something. |
| | | * |
| | | * @param subject |
| | | * @param message |
| | | * @param toAddresses |
| | | */ |
| | | @Override |
| | | public void sendMail(String subject, String message, Collection<String> toAddresses) { |
| | | this.sendMail(subject, message, toAddresses.toArray(new String[0])); |
| | | } |
| | | |
| | | /** |
| | | * Notify users by email of something. |
| | | * |
| | | * @param subject |
| | | * @param message |
| | | * @param toAddresses |
| | | */ |
| | | @Override |
| | | public void sendMail(String subject, String message, String... toAddresses) { |
| | | if (toAddresses == null || toAddresses.length == 0) { |
| | | logger.debug(MessageFormat.format("Dropping message {0} because there are no recipients", subject)); |
| | | return; |
| | | } |
| | | try { |
| | | Message mail = mailExecutor.createMessage(toAddresses); |
| | | if (mail != null) { |
| | | mail.setSubject(subject); |
| | | |
| | | MimeBodyPart messagePart = new MimeBodyPart(); |
| | | messagePart.setText(message, "utf-8"); |
| | | messagePart.setHeader("Content-Type", "text/plain; charset=\"utf-8\""); |
| | | messagePart.setHeader("Content-Transfer-Encoding", "quoted-printable"); |
| | | |
| | | MimeMultipart multiPart = new MimeMultipart(); |
| | | multiPart.addBodyPart(messagePart); |
| | | mail.setContent(multiPart); |
| | | |
| | | mailExecutor.queue(mail); |
| | | } |
| | | } catch (MessagingException e) { |
| | | logger.error("Messaging error", e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Notify users by email of something. |
| | | * |
| | | * @param subject |
| | | * @param message |
| | | * @param toAddresses |
| | | */ |
| | | @Override |
| | | public void sendHtmlMail(String subject, String message, Collection<String> toAddresses) { |
| | | this.sendHtmlMail(subject, message, toAddresses.toArray(new String[0])); |
| | | } |
| | | |
| | | /** |
| | | * Notify users by email of something. |
| | | * |
| | | * @param subject |
| | | * @param message |
| | | * @param toAddresses |
| | | */ |
| | | @Override |
| | | public void sendHtmlMail(String subject, String message, String... toAddresses) { |
| | | if (toAddresses == null || toAddresses.length == 0) { |
| | | logger.debug(MessageFormat.format("Dropping message {0} because there are no recipients", subject)); |
| | | return; |
| | | } |
| | | try { |
| | | Message mail = mailExecutor.createMessage(toAddresses); |
| | | if (mail != null) { |
| | | mail.setSubject(subject); |
| | | |
| | | MimeBodyPart messagePart = new MimeBodyPart(); |
| | | messagePart.setText(message, "utf-8"); |
| | | messagePart.setHeader("Content-Type", "text/html; charset=\"utf-8\""); |
| | | messagePart.setHeader("Content-Transfer-Encoding", "quoted-printable"); |
| | | |
| | | MimeMultipart multiPart = new MimeMultipart(); |
| | | multiPart.addBodyPart(messagePart); |
| | | mail.setContent(multiPart); |
| | | |
| | | mailExecutor.queue(mail); |
| | | } |
| | | } catch (MessagingException e) { |
| | | logger.error("Messaging error", e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Returns the descriptions/comments of the Gitblit config settings. |
| | | * |
| | | * @return SettingsModel |
| | | */ |
| | | @Override |
| | | public ServerSettings getSettingsModel() { |
| | | // ensure that the current values are updated in the setting models |
| | | for (String key : settings.getAllKeys(null)) { |
| | | SettingModel setting = settingsModel.get(key); |
| | | if (setting == null) { |
| | | // unreferenced setting, create a setting model |
| | | setting = new SettingModel(); |
| | | setting.name = key; |
| | | settingsModel.add(setting); |
| | | } |
| | | setting.currentValue = settings.getString(key, ""); |
| | | } |
| | | settingsModel.pushScripts = getAllScripts(); |
| | | return settingsModel; |
| | | } |
| | | |
| | | /** |
| | | * 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 = new ServerSettings(); |
| | | settingsModel.supportsCredentialChanges = userService.supportsCredentialChanges(); |
| | | settingsModel.supportsDisplayNameChanges = userService.supportsDisplayNameChanges(); |
| | | settingsModel.supportsEmailAddressChanges = userService.supportsEmailAddressChanges(); |
| | | settingsModel.supportsTeamMembershipChanges = userService.supportsTeamMembershipChanges(); |
| | | 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 |
| | |
| | | logger.error("Failed to load resource copy of gitblit.properties"); |
| | | } |
| | | return settingsModel; |
| | | } |
| | | |
| | | /** |
| | | * Configure the Gitblit singleton with the specified settings source. This |
| | | * source may be file settings (Gitblit GO) or may be web.xml settings |
| | | * (Gitblit WAR). |
| | | * |
| | | * @param settings |
| | | */ |
| | | public void configureContext(IStoredSettings settings, File folder, boolean startFederation) { |
| | | this.settings = settings; |
| | | this.baseFolder = folder; |
| | | |
| | | repositoriesFolder = getRepositoriesFolder(); |
| | | |
| | | logger.info("Gitblit base folder = " + folder.getAbsolutePath()); |
| | | logger.info("Git repositories folder = " + repositoriesFolder.getAbsolutePath()); |
| | | logger.info("Gitblit settings = " + settings.toString()); |
| | | |
| | | // prepare service executors |
| | | mailExecutor = new MailExecutor(settings); |
| | | luceneExecutor = new LuceneExecutor(settings, getManager(IRepositoryManager.class)); |
| | | gcExecutor = new GCExecutor(settings, getManager(IRepositoryManager.class)); |
| | | mirrorExecutor = new MirrorExecutor(settings, getManager(IRepositoryManager.class)); |
| | | |
| | | // initialize utilities |
| | | String prefix = settings.getString(Keys.git.userRepositoryPrefix, "~"); |
| | | ModelUtils.setUserRepoPrefix(prefix); |
| | | |
| | | // calculate repository list settings checksum for future config changes |
| | | repositoryListSettingsChecksum.set(getRepositoryListSettingsChecksum()); |
| | | |
| | | // build initial repository list |
| | | if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) { |
| | | logger.info("Identifying available repositories..."); |
| | | getRepositoryList(); |
| | | } |
| | | |
| | | logTimezone("JVM", TimeZone.getDefault()); |
| | | logTimezone(Constants.NAME, getTimezone()); |
| | | |
| | | serverStatus = new ServerStatus(goSettings != null); |
| | | |
| | | if (this.userService == null) { |
| | | String realm = settings.getString(Keys.realm.userService, "${baseFolder}/users.properties"); |
| | | IUserService loginService = null; |
| | | try { |
| | | // check to see if this "file" is a login service class |
| | | Class<?> realmClass = Class.forName(realm); |
| | | loginService = (IUserService) realmClass.newInstance(); |
| | | } catch (Throwable t) { |
| | | loginService = new GitblitUserService(); |
| | | } |
| | | setUserService(loginService); |
| | | } |
| | | |
| | | // load and cache the project metadata |
| | | projectConfigs = new FileBasedConfig(getFileOrFolder(Keys.web.projectsFile, "${baseFolder}/projects.conf"), FS.detect()); |
| | | getProjectConfigs(); |
| | | |
| | | configureMailExecutor(); |
| | | configureLuceneIndexing(); |
| | | configureGarbageCollector(); |
| | | configureMirrorExecutor(); |
| | | if (startFederation) { |
| | | configureFederation(); |
| | | } |
| | | configureJGit(); |
| | | configureFanout(); |
| | | configureGitDaemon(); |
| | | configureCommitCache(); |
| | | } |
| | | |
| | | protected void configureMailExecutor() { |
| | | if (mailExecutor.isReady()) { |
| | | logger.info("Mail executor is scheduled to process the message queue every 2 minutes."); |
| | | scheduledExecutor.scheduleAtFixedRate(mailExecutor, 1, 2, TimeUnit.MINUTES); |
| | | } else { |
| | | logger.warn("Mail server is not properly configured. Mail services disabled."); |
| | | } |
| | | } |
| | | |
| | | protected void configureLuceneIndexing() { |
| | |
| | | String bindInterface = settings.getString(Keys.git.daemonBindInterface, "localhost"); |
| | | if (port > 0) { |
| | | try { |
| | | gitDaemon = new GitDaemon(this, this, this, this); |
| | | // HACK temporary pending manager separation and injection |
| | | Gitblit gitblit = new Gitblit( |
| | | getManager(IRuntimeManager.class), |
| | | getManager(INotificationManager.class), |
| | | getManager(IUserManager.class), |
| | | getManager(ISessionManager.class), |
| | | this, |
| | | this, |
| | | this, |
| | | this); |
| | | gitDaemon = new GitDaemon(gitblit); |
| | | gitDaemon.start(); |
| | | } catch (IOException e) { |
| | | gitDaemon = null; |
| | |
| | | return luceneExecutor; |
| | | } |
| | | |
| | | private void logTimezone(String type, TimeZone zone) { |
| | | SimpleDateFormat df = new SimpleDateFormat("z Z"); |
| | | df.setTimeZone(zone); |
| | | String offset = df.format(new Date()); |
| | | logger.info(type + " timezone is " + zone.getID() + " (" + offset + ")"); |
| | | } |
| | | |
| | | /** |
| | | * Configure Gitblit from the web.xml, if no configuration has already been |
| | | * specified. |
| | |
| | | */ |
| | | @Override |
| | | protected void beforeServletInjection(ServletContext context) { |
| | | if (settings == null) { |
| | | // Gitblit is running in a servlet container |
| | | ObjectGraph injector = getInjector(context); |
| | | |
| | | // 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 |
| | | WebXmlSettings webxmlSettings = new WebXmlSettings(context); |
| | | String contextRealPath = context.getRealPath("/"); |
| | | File contextFolder = (contextRealPath != null) ? new File(contextRealPath) : null; |
| | | String openShift = System.getenv("OPENSHIFT_DATA_DIR"); |
| | | |
| | | if (!StringUtils.isEmpty(openShift)) { |
| | | // Gitblit is running in OpenShift/JBoss |
| | | File base = new File(openShift); |
| | | logger.info("EXPRESS contextFolder is " + contextFolder.getAbsolutePath()); |
| | | |
| | | // gitblit.properties setting overrides |
| | | File overrideFile = new File(base, "gitblit.properties"); |
| | | webxmlSettings.applyOverrides(overrideFile); |
| | | |
| | | // Copy the included scripts to the configured groovy folder |
| | | String path = webxmlSettings.getString(Keys.groovy.scriptsFolder, "groovy"); |
| | | File localScripts = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, base, path); |
| | | if (!localScripts.exists()) { |
| | | File warScripts = new File(contextFolder, "/WEB-INF/data/groovy"); |
| | | if (!warScripts.equals(localScripts)) { |
| | | try { |
| | | com.gitblit.utils.FileUtils.copy(localScripts, warScripts.listFiles()); |
| | | } catch (IOException e) { |
| | | logger.error(MessageFormat.format( |
| | | "Failed to copy included Groovy scripts from {0} to {1}", |
| | | warScripts, localScripts)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // disable Git daemon on Express - we can't bind 9418 and we |
| | | // can't port-forward to the daemon |
| | | webxmlSettings.overrideSetting(Keys.git.daemonPort, 0); |
| | | |
| | | // configure context using the web.xml |
| | | configureContext(webxmlSettings, base, true); |
| | | if (!StringUtils.isEmpty(System.getenv("OPENSHIFT_DATA_DIR"))) { |
| | | // RedHat OpenShift |
| | | logger.debug("configuring Gitblit Express"); |
| | | baseFolder = configureExpress(context, webxmlSettings, contextFolder, runtimeSettings); |
| | | } else { |
| | | // Gitblit is running in a standard servlet container |
| | | logger.info("WAR contextFolder is " + ((contextFolder != null) ? contextFolder.getAbsolutePath() : "<empty>")); |
| | | |
| | | String path = webxmlSettings.getString(Constants.baseFolder, Constants.contextFolder$ + "/WEB-INF/data"); |
| | | |
| | | if (path.contains(Constants.contextFolder$) && contextFolder == null) { |
| | | // warn about null contextFolder (issue-199) |
| | | logger.error(""); |
| | | logger.error(MessageFormat.format("\"{0}\" depends on \"{1}\" but \"{2}\" is returning NULL for \"{1}\"!", |
| | | Constants.baseFolder, Constants.contextFolder$, context.getServerInfo())); |
| | | logger.error(MessageFormat.format("Please specify a non-parameterized path for <context-param> {0} in web.xml!!", Constants.baseFolder)); |
| | | logger.error(MessageFormat.format("OR configure your servlet container to specify a \"{0}\" parameter in the context configuration!!", Constants.baseFolder)); |
| | | logger.error(""); |
| | | } |
| | | |
| | | try { |
| | | // try to lookup JNDI env-entry for the baseFolder |
| | | InitialContext ic = new InitialContext(); |
| | | Context env = (Context) ic.lookup("java:comp/env"); |
| | | String val = (String) env.lookup("baseFolder"); |
| | | if (!StringUtils.isEmpty(val)) { |
| | | path = val; |
| | | } |
| | | } catch (NamingException n) { |
| | | logger.error("Failed to get JNDI env-entry: " + n.getExplanation()); |
| | | } |
| | | |
| | | File base = com.gitblit.utils.FileUtils.resolveParameter(Constants.contextFolder$, contextFolder, path); |
| | | base.mkdirs(); |
| | | |
| | | // try to extract the data folder resource to the baseFolder |
| | | File localSettings = new File(base, "gitblit.properties"); |
| | | if (!localSettings.exists()) { |
| | | extractResources(context, "/WEB-INF/data/", base); |
| | | } |
| | | |
| | | // delegate all config to baseFolder/gitblit.properties file |
| | | FileSettings settings = new FileSettings(localSettings.getAbsolutePath()); |
| | | configureContext(settings, base, true); |
| | | // standard WAR |
| | | logger.debug("configuring Gitblit WAR"); |
| | | baseFolder = configureWAR(context, webxmlSettings, contextFolder, runtimeSettings); |
| | | } |
| | | |
| | | // WAR or Express is likely to be running on a Tomcat. |
| | | // Test for the forward-slash/%2F issue and auto-adjust settings. |
| | | ContainerUtils.CVE_2007_0450.test(settings); |
| | | // Test for Tomcat forward-slash/%2F issue and auto-adjust settings |
| | | ContainerUtils.CVE_2007_0450.test(runtimeSettings); |
| | | } |
| | | |
| | | settingsModel = loadSettingModels(); |
| | | serverStatus.servletContainer = context.getServerInfo(); |
| | | // Runtime manager is a container for settings and other parameters |
| | | IRuntimeManager runtime = startManager(injector, IRuntimeManager.class); |
| | | runtime.setBaseFolder(baseFolder); |
| | | runtime.getStatus().isGO = goSettings != null; |
| | | runtime.getStatus().servletContainer = context.getServerInfo(); |
| | | |
| | | startManager(injector, INotificationManager.class); |
| | | startManager(injector, IUserManager.class); |
| | | startManager(injector, ISessionManager.class); |
| | | |
| | | repositoriesFolder = getRepositoriesFolder(); |
| | | |
| | | logger.info("Gitblit base folder = " + baseFolder.getAbsolutePath()); |
| | | logger.info("Git repositories folder = " + repositoriesFolder.getAbsolutePath()); |
| | | |
| | | // prepare service executors |
| | | luceneExecutor = new LuceneExecutor(runtimeSettings, getManager(IRepositoryManager.class)); |
| | | gcExecutor = new GCExecutor(runtimeSettings, getManager(IRepositoryManager.class)); |
| | | mirrorExecutor = new MirrorExecutor(runtimeSettings, getManager(IRepositoryManager.class)); |
| | | |
| | | // initialize utilities |
| | | String prefix = runtimeSettings.getString(Keys.git.userRepositoryPrefix, "~"); |
| | | ModelUtils.setUserRepoPrefix(prefix); |
| | | |
| | | // calculate repository list settings checksum for future config changes |
| | | repositoryListSettingsChecksum.set(getRepositoryListSettingsChecksum()); |
| | | |
| | | // build initial repository list |
| | | if (runtimeSettings.getBoolean(Keys.git.cacheRepositoryList, true)) { |
| | | logger.info("Identifying available repositories..."); |
| | | getRepositoryList(); |
| | | } |
| | | |
| | | loadSettingModels(runtime.getSettingsModel()); |
| | | |
| | | // load and cache the project metadata |
| | | projectConfigs = new FileBasedConfig(runtime.getFileOrFolder(Keys.web.projectsFile, "${baseFolder}/projects.conf"), FS.detect()); |
| | | getProjectConfigs(); |
| | | |
| | | configureLuceneIndexing(); |
| | | configureGarbageCollector(); |
| | | configureMirrorExecutor(); |
| | | if (true/*startFederation*/) { |
| | | configureFederation(); |
| | | } |
| | | configureJGit(); |
| | | configureFanout(); |
| | | configureGitDaemon(); |
| | | configureCommitCache(); |
| | | } |
| | | |
| | | /** |
| | | * Configures Gitblit GO |
| | | * |
| | | * @param context |
| | | * @param settings |
| | | * @param baseFolder |
| | | * @param runtimeSettings |
| | | * @return the base folder |
| | | */ |
| | | protected File configureGO( |
| | | ServletContext context, |
| | | IStoredSettings goSettings, |
| | | File goBaseFolder, |
| | | IStoredSettings runtimeSettings) { |
| | | |
| | | // merge the stored settings into the runtime settings |
| | | // |
| | | // if runtimeSettings is also a FileSettings w/o a specified target file, |
| | | // the target file for runtimeSettings is set to "localSettings". |
| | | runtimeSettings.merge(goSettings); |
| | | File base = goBaseFolder; |
| | | return base; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Configures a standard WAR instance of Gitblit. |
| | | * |
| | | * @param context |
| | | * @param webxmlSettings |
| | | * @param contextFolder |
| | | * @param runtimeSettings |
| | | * @return the base folder |
| | | */ |
| | | protected File configureWAR( |
| | | ServletContext context, |
| | | WebXmlSettings webxmlSettings, |
| | | File contextFolder, |
| | | IStoredSettings runtimeSettings) { |
| | | |
| | | // Gitblit is running in a standard servlet container |
| | | logger.info("WAR contextFolder is " + ((contextFolder != null) ? contextFolder.getAbsolutePath() : "<empty>")); |
| | | |
| | | String path = webxmlSettings.getString(Constants.baseFolder, Constants.contextFolder$ + "/WEB-INF/data"); |
| | | |
| | | if (path.contains(Constants.contextFolder$) && contextFolder == null) { |
| | | // warn about null contextFolder (issue-199) |
| | | logger.error(""); |
| | | logger.error(MessageFormat.format("\"{0}\" depends on \"{1}\" but \"{2}\" is returning NULL for \"{1}\"!", |
| | | Constants.baseFolder, Constants.contextFolder$, context.getServerInfo())); |
| | | logger.error(MessageFormat.format("Please specify a non-parameterized path for <context-param> {0} in web.xml!!", Constants.baseFolder)); |
| | | logger.error(MessageFormat.format("OR configure your servlet container to specify a \"{0}\" parameter in the context configuration!!", Constants.baseFolder)); |
| | | logger.error(""); |
| | | } |
| | | |
| | | try { |
| | | // try to lookup JNDI env-entry for the baseFolder |
| | | InitialContext ic = new InitialContext(); |
| | | Context env = (Context) ic.lookup("java:comp/env"); |
| | | String val = (String) env.lookup("baseFolder"); |
| | | if (!StringUtils.isEmpty(val)) { |
| | | path = val; |
| | | } |
| | | } catch (NamingException n) { |
| | | logger.error("Failed to get JNDI env-entry: " + n.getExplanation()); |
| | | } |
| | | |
| | | File base = com.gitblit.utils.FileUtils.resolveParameter(Constants.contextFolder$, contextFolder, path); |
| | | base.mkdirs(); |
| | | |
| | | // try to extract the data folder resource to the baseFolder |
| | | File localSettings = new File(base, "gitblit.properties"); |
| | | if (!localSettings.exists()) { |
| | | extractResources(context, "/WEB-INF/data/", base); |
| | | } |
| | | |
| | | // delegate all config to baseFolder/gitblit.properties file |
| | | FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath()); |
| | | |
| | | // merge the stored settings into the runtime settings |
| | | // |
| | | // if runtimeSettings is also a FileSettings w/o a specified target file, |
| | | // the target file for runtimeSettings is set to "localSettings". |
| | | runtimeSettings.merge(fileSettings); |
| | | |
| | | return base; |
| | | } |
| | | |
| | | /** |
| | | * Configures an OpenShift instance of Gitblit. |
| | | * |
| | | * @param context |
| | | * @param webxmlSettings |
| | | * @param contextFolder |
| | | * @param runtimeSettings |
| | | * @return the base folder |
| | | */ |
| | | private File configureExpress( |
| | | ServletContext context, |
| | | WebXmlSettings webxmlSettings, |
| | | File contextFolder, |
| | | IStoredSettings runtimeSettings) { |
| | | |
| | | // Gitblit is running in OpenShift/JBoss |
| | | String openShift = System.getenv("OPENSHIFT_DATA_DIR"); |
| | | File base = new File(openShift); |
| | | logger.info("EXPRESS contextFolder is " + contextFolder.getAbsolutePath()); |
| | | |
| | | // Copy the included scripts to the configured groovy folder |
| | | String path = webxmlSettings.getString(Keys.groovy.scriptsFolder, "groovy"); |
| | | File localScripts = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, base, path); |
| | | if (!localScripts.exists()) { |
| | | File warScripts = new File(contextFolder, "/WEB-INF/data/groovy"); |
| | | if (!warScripts.equals(localScripts)) { |
| | | try { |
| | | com.gitblit.utils.FileUtils.copy(localScripts, warScripts.listFiles()); |
| | | } catch (IOException e) { |
| | | logger.error(MessageFormat.format( |
| | | "Failed to copy included Groovy scripts from {0} to {1}", |
| | | warScripts, localScripts)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // merge the WebXmlSettings into the runtime settings (for backwards-compatibilty) |
| | | runtimeSettings.merge(webxmlSettings); |
| | | |
| | | // settings are to be stored in openshift/gitblit.properties |
| | | File localSettings = new File(base, "gitblit.properties"); |
| | | FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath()); |
| | | |
| | | // merge the stored settings into the runtime settings |
| | | // |
| | | // if runtimeSettings is also a FileSettings w/o a specified target file, |
| | | // the target file for runtimeSettings is set to "localSettings". |
| | | runtimeSettings.merge(fileSettings); |
| | | |
| | | return base; |
| | | } |
| | | |
| | | protected void extractResources(ServletContext context, String path, File toDir) { |
| | |
| | | @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(); |
| | | luceneExecutor.close(); |
| | | gcExecutor.close(); |
| | |
| | | // 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 = getUserModel(owner); |
| | | UserModel originOwner = getManager(IUserManager.class).getUserModel(owner); |
| | | if (originOwner != null) { |
| | | originOwner.setRepositoryPermission(cloneName, AccessPermission.CLONE); |
| | | updateUserModel(originOwner.username, originOwner, false); |
| | |
| | | List<UserModel> cloneUsers = new ArrayList<UserModel>(); |
| | | for (String name : users) { |
| | | if (!name.equalsIgnoreCase(user.username)) { |
| | | UserModel cloneUser = getUserModel(name); |
| | | 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); |
| | | } |
| | | } |
| | | userService.updateUserModels(cloneUsers); |
| | | getManager(IUserManager.class).updateUserModels(cloneUsers); |
| | | |
| | | // grant origin's team list clone permission to fork |
| | | List<String> teams = getRepositoryTeams(repository); |
| | | List<TeamModel> cloneTeams = new ArrayList<TeamModel>(); |
| | | for (String name : teams) { |
| | | TeamModel cloneTeam = getTeamModel(name); |
| | | 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); |
| | | } |
| | | userService.updateTeamModels(cloneTeams); |
| | | getManager(IUserManager.class).updateTeamModels(cloneTeams); |
| | | |
| | | // add this clone to the cached model |
| | | addToCachedRepositoryList(cloneModel); |
| | | return cloneModel; |
| | | } |
| | | |
| | | /** |
| | | * Allow to understand if GitBlit supports and is configured to allow |
| | | * cookie-based authentication. |
| | | * |
| | | * @return status of Cookie authentication enablement. |
| | | */ |
| | | @Override |
| | | public boolean supportsCookies() { |
| | | return settings.getBoolean(Keys.web.allowCookieAuthentication, true) && userService.supportsCookies(); |
| | | } |
| | | |
| | | @Override |
| | | public String getCookie(UserModel model) { |
| | | return userService.getCookie(model); |
| | | } |
| | | |
| | | @Override |
| | | public UserModel authenticate(char[] cookie) { |
| | | return userService.authenticate(cookie); |
| | | } |
| | | |
| | | @Override |
| | | public boolean updateUserModel(UserModel model) { |
| | | return userService.updateUserModel(model); |
| | | } |
| | | |
| | | @Override |
| | | public boolean updateUserModels(Collection<UserModel> models) { |
| | | return userService.updateUserModels(models); |
| | | } |
| | | |
| | | @Override |
| | | public boolean updateUserModel(String username, UserModel model) { |
| | | return userService.updateUserModel(username, model); |
| | | } |
| | | |
| | | @Override |
| | | public boolean deleteUserModel(UserModel model) { |
| | | return userService.deleteUserModel(model); |
| | | } |
| | | |
| | | @Override |
| | | public List<String> getAllTeamNames() { |
| | | return userService.getAllTeamNames(); |
| | | } |
| | | |
| | | @Override |
| | | public List<String> getTeamnamesForRepositoryRole(String role) { |
| | | return userService.getTeamnamesForRepositoryRole(role); |
| | | } |
| | | |
| | | @Override |
| | | public boolean updateTeamModel(TeamModel model) { |
| | | return userService.updateTeamModel(model); |
| | | } |
| | | |
| | | @Override |
| | | public boolean updateTeamModels(Collection<TeamModel> models) { |
| | | return userService.updateTeamModels(models); |
| | | } |
| | | |
| | | @Override |
| | | public boolean updateTeamModel(String teamname, TeamModel model) { |
| | | return userService.updateTeamModel(teamname, model); |
| | | } |
| | | |
| | | @Override |
| | | public boolean deleteTeamModel(TeamModel model) { |
| | | return userService.deleteTeamModel(model); |
| | | } |
| | | |
| | | @Override |
| | | public List<String> getUsernamesForRepositoryRole(String role) { |
| | | return userService.getUsernamesForRepositoryRole(role); |
| | | } |
| | | |
| | | @Override |
| | | public boolean renameRepositoryRole(String oldRole, String newRole) { |
| | | return userService.renameRepositoryRole(oldRole, newRole); |
| | | } |
| | | |
| | | @Override |
| | | public boolean deleteRepositoryRole(String role) { |
| | | return userService.deleteRepositoryRole(role); |
| | | } |
| | | |
| | | @Override |
| | | public void logout(HttpServletResponse response, UserModel user) { |
| | | setCookie(response, null); |
| | | userService.logout(user); |
| | | } |
| | | |
| | | @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. |