diff options
Diffstat (limited to 'src')
45 files changed, 1734 insertions, 468 deletions
diff --git a/src/main/bugtraq/com/syntevo/bugtraq/BugtraqConfig.java b/src/main/bugtraq/com/syntevo/bugtraq/BugtraqConfig.java index 7776e6fe..60b4ecc0 100644 --- a/src/main/bugtraq/com/syntevo/bugtraq/BugtraqConfig.java +++ b/src/main/bugtraq/com/syntevo/bugtraq/BugtraqConfig.java @@ -214,7 +214,7 @@ public final class BugtraqConfig { } finally { rw.dispose(); - tw.release(); + tw.close(); } if (content == null) { diff --git a/src/main/distrib/data/defaults.properties b/src/main/distrib/data/defaults.properties index 0857ccf6..ae519a10 100644 --- a/src/main/distrib/data/defaults.properties +++ b/src/main/distrib/data/defaults.properties @@ -126,6 +126,28 @@ git.sshKeysManager = com.gitblit.transport.ssh.FileKeyManager # SINCE 1.5.0 git.sshKeysFolder= ${baseFolder}/ssh +# Use kerberos5 (GSS) authentication +# +# SINCE 1.7.0 +git.sshWithKrb5 = "false" + +# The path to a kerberos 5 keytab. +# +# SINCE 1.7.0 +git.sshKrb5Keytab = "" + +# The service principal name to be used for Kerberos5. The default is host/hostname. +# +# SINCE 1.7.0 +git.sshKrb5ServicePrincipalName = "" + +# A comma-separated list of authentication method. They will be tried in +# the given order. Possible values are +# "gssapi-with-mic", "publickey", "keyboard-interactive" or "password" +# +# SINCE 1.7.0 +git.sshAuthenticatorsOrder = "password,keyboard-interactive,publickey" + # SSH backend NIO2|MINA. # # The Apache Mina project recommends using the NIO2 backend. @@ -762,6 +784,11 @@ web.includePersonalRepositories = false # BASEFOLDER web.projectsFile = ${baseFolder}/projects.conf +# Defines the tab length for all blob views +# +# SINCE 1.7.0 +web.tabLength = 4 + # Either the full path to a user config file (users.conf) # OR a fully qualified class name that implements the IUserService interface. # @@ -1141,6 +1168,24 @@ web.forwardSlashCharacter = / # SINCE 0.5.0 web.otherUrls = +# Should HTTP/HTTPS URLs be displayed if the git servlet is enabled? +# default: true +# +# SINCE 1.7.0 +web.showHttpServletUrls = true + +# Should git URLs be displayed if the git daemon is enabled? +# default: true +# +# SINCE 1.7.0 +web.showGitDaemonUrls = true + +# Should SSH URLs be displayed if the SSH daemon is enabled? +# default: true +# +# SINCE 1.7.0 +web.showSshDaemonUrls = true + # Should app-specific clone links be displayed for SourceTree, SparkleShare, etc? # # SINCE 1.3.0 @@ -1610,6 +1655,22 @@ federation.sets = # SINCE 1.3.0 realm.container.autoCreateAccounts = false +# A set of mapping used to map HTTP session attributes to user informations +# They are used if realm.container.autoCreateAccounts is set to true and +# the webapp container used can fill the session with user informations +# +# SINCE 1.7.0 +realm.container.autoAccounts.displayName = +realm.container.autoAccounts.emailAddress = +realm.container.autoAccounts.locale = + +# If the user's created by the webapp container is given this role, +# the user created will be a admin user. +# +# SINCE 1.7.0 +realm.container.autoAccounts.adminRole = + + # Allow or prohibit Windows guest account logins # # SINCE 1.3.0 diff --git a/src/main/java/com/gitblit/extensions/RepositoryLifeCycleListener.java b/src/main/java/com/gitblit/extensions/RepositoryLifeCycleListener.java index 5ef03af7..e23fca78 100644 --- a/src/main/java/com/gitblit/extensions/RepositoryLifeCycleListener.java +++ b/src/main/java/com/gitblit/extensions/RepositoryLifeCycleListener.java @@ -36,6 +36,24 @@ public abstract class RepositoryLifeCycleListener implements ExtensionPoint { public abstract void onCreation(RepositoryModel repository); /** + * Called after a repository has been forked. + * + * @param origin + * @param fork + * @since 1.7.0 + */ + public abstract void onFork(RepositoryModel origin, RepositoryModel fork); + + /** + * Called after a repository has been renamed. + * + * @param oldName + * @param repository + * @since 1.7.0 + */ + public abstract void onRename(String oldName, RepositoryModel repository); + + /** * Called after a repository has been deleted. * * @param repository diff --git a/src/main/java/com/gitblit/git/PatchsetReceivePack.java b/src/main/java/com/gitblit/git/PatchsetReceivePack.java index 54ffb7ba..ef0b409b 100644 --- a/src/main/java/com/gitblit/git/PatchsetReceivePack.java +++ b/src/main/java/com/gitblit/git/PatchsetReceivePack.java @@ -667,7 +667,7 @@ public class PatchsetReceivePack extends GitblitReceivePack { // identified the missing object earlier before we got control.
LOGGER.error("failed to get commit count", e);
} finally {
- walk.release();
+ walk.close();
}
sendError("");
@@ -1078,7 +1078,7 @@ public class PatchsetReceivePack extends GitblitReceivePack { LOGGER.error("failed to get commit count", e);
return 0;
} finally {
- walk.release();
+ walk.close();
}
return count;
}
diff --git a/src/main/java/com/gitblit/guice/WebModule.java b/src/main/java/com/gitblit/guice/WebModule.java index 5b569182..c6172c3d 100644 --- a/src/main/java/com/gitblit/guice/WebModule.java +++ b/src/main/java/com/gitblit/guice/WebModule.java @@ -19,6 +19,7 @@ import java.util.HashMap; import java.util.Map; import com.gitblit.Constants; +import com.gitblit.servlet.AccessDeniedServlet; import com.gitblit.servlet.BranchGraphServlet; import com.gitblit.servlet.DownloadZipFilter; import com.gitblit.servlet.DownloadZipServlet; @@ -70,6 +71,17 @@ public class WebModule extends ServletModule { serve("/robots.txt").with(RobotsTxtServlet.class); serve("/logo.png").with(LogoServlet.class); + /* Prevent accidental access to 'resources' such as GitBlit java classes + * + * In the GO setup the JAR containing the application and the WAR injected + * into Jetty are the same file. However Jetty expects to serve the entire WAR + * contents, except the WEB-INF folder. Thus, all java binary classes in the + * JAR are served by default as is they were legitimate resources. + * + * The below servlet mappings prevent that behavior + */ + serve(fuzzy("/com/")).with(AccessDeniedServlet.class); + // global filters filter(ALL).through(ProxyFilter.class); filter(ALL).through(EnforceAuthenticationFilter.class); diff --git a/src/main/java/com/gitblit/manager/AuthenticationManager.java b/src/main/java/com/gitblit/manager/AuthenticationManager.java index 29221e6f..38e45a6f 100644 --- a/src/main/java/com/gitblit/manager/AuthenticationManager.java +++ b/src/main/java/com/gitblit/manager/AuthenticationManager.java @@ -215,6 +215,29 @@ public class AuthenticationManager implements IAuthenticationManager { user.displayName = username; user.password = Constants.EXTERNAL_ACCOUNT; user.accountType = AccountType.CONTAINER; + + // Try to extract user's informations for the session + // it uses "realm.container.autoAccounts.*" as the attribute name to look for + HttpSession session = httpRequest.getSession(); + String emailAddress = resolveAttribute(session, Keys.realm.container.autoAccounts.emailAddress); + if(emailAddress != null) { + user.emailAddress = emailAddress; + } + String displayName = resolveAttribute(session, Keys.realm.container.autoAccounts.displayName); + if(displayName != null) { + user.displayName = displayName; + } + String userLocale = resolveAttribute(session, Keys.realm.container.autoAccounts.locale); + if(userLocale != null) { + user.getPreferences().setLocale(userLocale); + } + String adminRole = settings.getString(Keys.realm.container.autoAccounts.adminRole, null); + if(adminRole != null && ! adminRole.isEmpty()) { + if(httpRequest.isUserInRole(adminRole)) { + user.canAdmin = true; + } + } + userManager.updateUserModel(user); flagSession(httpRequest, AuthenticationType.CONTAINER); logger.debug(MessageFormat.format("{0} authenticated and created by servlet container principal from {1}", @@ -293,6 +316,31 @@ public class AuthenticationManager implements IAuthenticationManager { } return null; } + + /** + * Extract given attribute from the session and return it's content + * it return null if attributeMapping is empty, or if the value is + * empty + * + * @param session The user session + * @param attributeMapping + * @return + */ + private String resolveAttribute(HttpSession session, String attributeMapping) { + String attributeName = settings.getString(attributeMapping, null); + if(StringUtils.isEmpty(attributeName)) { + return null; + } + Object attributeValue = session.getAttribute(attributeName); + if(attributeValue == null) { + return null; + } + String value = attributeValue.toString(); + if(value.isEmpty()) { + return null; + } + return value; + } /** * Authenticate a user based on a public key. @@ -326,6 +374,35 @@ public class AuthenticationManager implements IAuthenticationManager { /** + * Return the UserModel for already authenticated user. + * + * This implementation assumes that the authentication has already take place + * (e.g. SSHDaemon) and that this is a validation/verification of the user. + * + * @param username + * @return a user object or null + */ + @Override + public UserModel authenticate(String username) { + if (username != null) { + if (!StringUtils.isEmpty(username)) { + UserModel user = userManager.getUserModel(username); + if (user != null) { + // existing user + logger.debug(MessageFormat.format("{0} authenticated externally", user.username)); + return validateAuthentication(user, AuthenticationType.CONTAINER); + } + logger.warn(MessageFormat.format("Failed to find UserModel for {0} during external authentication", + username)); + } + } else { + logger.warn("Empty user passed to AuthenticationManager.authenticate!"); + } + return null; + } + + + /** * This method allows the authentication manager to reject authentication * attempts. It is called after the username/secret have been verified to * ensure that the authentication technique has been logged. diff --git a/src/main/java/com/gitblit/manager/GitblitManager.java b/src/main/java/com/gitblit/manager/GitblitManager.java index 9692e654..8d25b3f5 100644 --- a/src/main/java/com/gitblit/manager/GitblitManager.java +++ b/src/main/java/com/gitblit/manager/GitblitManager.java @@ -54,6 +54,7 @@ import com.gitblit.Constants.FederationToken; import com.gitblit.Constants.Role; import com.gitblit.GitBlitException; import com.gitblit.IStoredSettings; +import com.gitblit.extensions.RepositoryLifeCycleListener; import com.gitblit.models.FederationModel; import com.gitblit.models.FederationProposal; import com.gitblit.models.FederationSet; @@ -276,6 +277,16 @@ public class GitblitManager implements IGitblit { // add this clone to the cached model repositoryManager.addToCachedRepositoryList(cloneModel); + + if (pluginManager != null) { + for (RepositoryLifeCycleListener listener : pluginManager.getExtensions(RepositoryLifeCycleListener.class)) { + try { + listener.onFork(repository, cloneModel); + } catch (Throwable t) { + logger.error(String.format("failed to call plugin onFork %s", repository.name), t); + } + } + } return cloneModel; } @@ -651,6 +662,11 @@ public class GitblitManager implements IGitblit { } @Override + public UserModel authenticate(String username) { + return authenticationManager.authenticate(username); + } + + @Override public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) { UserModel user = authenticationManager.authenticate(httpRequest, requiresCertificate); if (user == null) { diff --git a/src/main/java/com/gitblit/manager/IAuthenticationManager.java b/src/main/java/com/gitblit/manager/IAuthenticationManager.java index d48ec534..c81092b9 100644 --- a/src/main/java/com/gitblit/manager/IAuthenticationManager.java +++ b/src/main/java/com/gitblit/manager/IAuthenticationManager.java @@ -71,6 +71,16 @@ public interface IAuthenticationManager extends IManager { UserModel authenticate(String username, char[] password); /** + * Return the UserModel for already authenticated user. + * + * @see IUserService.authenticate(String, char[]) + * @param username + * @return a user object or null + * @since 1.7.0 + */ + UserModel authenticate(String username); + + /** * Returns the Gitlbit cookie in the request. * * @param request diff --git a/src/main/java/com/gitblit/manager/RepositoryManager.java b/src/main/java/com/gitblit/manager/RepositoryManager.java index c2c6088e..23fd0224 100644 --- a/src/main/java/com/gitblit/manager/RepositoryManager.java +++ b/src/main/java/com/gitblit/manager/RepositoryManager.java @@ -1375,6 +1375,7 @@ public class RepositoryManager implements IRepositoryManager { repository.name = repository.name.substring(projectPath.length() + 1); } } + boolean isRename = false; if (isCreate) { // ensure created repository name ends with .git if (!repository.name.toLowerCase().endsWith(org.eclipse.jgit.lib.Constants.DOT_GIT_EXT)) { @@ -1391,7 +1392,8 @@ public class RepositoryManager implements IRepositoryManager { r = JGitUtils.createRepository(repositoriesFolder, repository.name, shared); } else { // rename repository - if (!repositoryName.equalsIgnoreCase(repository.name)) { + isRename = !repositoryName.equalsIgnoreCase(repository.name); + if (isRename) { if (!repository.name.toLowerCase().endsWith( org.eclipse.jgit.lib.Constants.DOT_GIT_EXT)) { repository.name += org.eclipse.jgit.lib.Constants.DOT_GIT_EXT; @@ -1511,6 +1513,14 @@ public class RepositoryManager implements IRepositoryManager { logger.error(String.format("failed to call plugin onCreation %s", repositoryName), t); } } + } else if (isRename && pluginManager != null) { + for (RepositoryLifeCycleListener listener : pluginManager.getExtensions(RepositoryLifeCycleListener.class)) { + try { + listener.onRename(repositoryName, repository); + } catch (Throwable t) { + logger.error(String.format("failed to call plugin onRename %s", repositoryName), t); + } + } } } diff --git a/src/main/java/com/gitblit/manager/ServicesManager.java b/src/main/java/com/gitblit/manager/ServicesManager.java index 69e5e408..5a957a17 100644 --- a/src/main/java/com/gitblit/manager/ServicesManager.java +++ b/src/main/java/com/gitblit/manager/ServicesManager.java @@ -162,7 +162,8 @@ public class ServicesManager implements IServicesManager { List<RepositoryUrl> list = new ArrayList<RepositoryUrl>(); // http/https url - if (settings.getBoolean(Keys.git.enableGitServlet, true)) { + if (settings.getBoolean(Keys.git.enableGitServlet, true) && + settings.getBoolean(Keys.web.showHttpServletUrls, true)) { AccessPermission permission = user.getRepositoryPermission(repository).permission; if (permission.exceeds(AccessPermission.NONE)) { Transport transport = Transport.fromString(request.getScheme()); @@ -177,7 +178,8 @@ public class ServicesManager implements IServicesManager { // ssh daemon url String sshDaemonUrl = getSshDaemonUrl(request, user, repository); - if (!StringUtils.isEmpty(sshDaemonUrl)) { + if (!StringUtils.isEmpty(sshDaemonUrl) && + settings.getBoolean(Keys.web.showSshDaemonUrls, true)) { AccessPermission permission = user.getRepositoryPermission(repository).permission; if (permission.exceeds(AccessPermission.NONE)) { if (permission.atLeast(AccessPermission.PUSH) && !acceptsPush(Transport.SSH)) { @@ -192,7 +194,8 @@ public class ServicesManager implements IServicesManager { // git daemon url String gitDaemonUrl = getGitDaemonUrl(request, user, repository); - if (!StringUtils.isEmpty(gitDaemonUrl)) { + if (!StringUtils.isEmpty(gitDaemonUrl) && + settings.getBoolean(Keys.web.showGitDaemonUrls, true)) { AccessPermission permission = getGitDaemonAccessPermission(user, repository); if (permission.exceeds(AccessPermission.NONE)) { if (permission.atLeast(AccessPermission.PUSH) && !acceptsPush(Transport.GIT)) { diff --git a/src/main/java/com/gitblit/manager/UserManager.java b/src/main/java/com/gitblit/manager/UserManager.java index 86be8bcd..0d7ee9a5 100644 --- a/src/main/java/com/gitblit/manager/UserManager.java +++ b/src/main/java/com/gitblit/manager/UserManager.java @@ -83,9 +83,9 @@ public class UserManager implements IUserManager { * @param userService */ public void setUserService(IUserService userService) { - logger.info(userService.toString()); this.userService = userService; this.userService.setup(runtimeManager); + logger.info(userService.toString()); } @Override @@ -115,10 +115,12 @@ public class UserManager implements IUserManager { // check to see if this "file" is a custom user service class Class<?> realmClass = Class.forName(realm); service = (IUserService) realmClass.newInstance(); - } catch (Throwable t) { + } catch (ClassNotFoundException t) { // typical file path configuration File realmFile = runtimeManager.getFileOrFolder(Keys.realm.userService, "${baseFolder}/users.conf"); service = createUserService(realmFile); + } catch (InstantiationException | IllegalAccessException e) { + logger.error("failed to instanciate user service {}: {}", realm, e.getMessage()); } } setUserService(service); diff --git a/src/main/java/com/gitblit/service/LuceneService.java b/src/main/java/com/gitblit/service/LuceneService.java index 3f7635f4..285ea8d7 100644 --- a/src/main/java/com/gitblit/service/LuceneService.java +++ b/src/main/java/com/gitblit/service/LuceneService.java @@ -615,7 +615,7 @@ public class LuceneService implements Runnable { }
// finished
- reader.release();
+ reader.close();
// commit all changes and reset the searcher
config.setInt(CONF_INDEX, null, CONF_VERSION, INDEX_VERSION);
@@ -1104,6 +1104,7 @@ public class LuceneService implements Runnable { content = "";
}
+ int tabLength = storedSettings.getInteger(Keys.web.tabLength, 4);
int fragmentLength = SearchObjectType.commit == result.type ? 512 : 150;
QueryScorer scorer = new QueryScorer(query, "content");
@@ -1126,7 +1127,7 @@ public class LuceneService implements Runnable { if (fragment.length() > fragmentLength) {
fragment = fragment.substring(0, fragmentLength) + "...";
}
- return "<pre class=\"text\">" + StringUtils.escapeForHtml(fragment, true) + "</pre>";
+ return "<pre class=\"text\">" + StringUtils.escapeForHtml(fragment, true, tabLength) + "</pre>";
}
// make sure we have unique fragments
diff --git a/src/main/java/com/gitblit/servlet/AccessDeniedServlet.java b/src/main/java/com/gitblit/servlet/AccessDeniedServlet.java new file mode 100644 index 00000000..ae0797e2 --- /dev/null +++ b/src/main/java/com/gitblit/servlet/AccessDeniedServlet.java @@ -0,0 +1,63 @@ +/* + * Copyright 2015 Jean-Baptiste Mayer + * + * 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.servlet; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.google.inject.Singleton; + +/** + * Access-denied Servlet. + * + * This servlet serves only 404 Not Found error replies. + * + * This servlet is used to override the container's default behavior to serve + * all contents of the application's WAR. We can selectively prevent access to + * a specific path simply by mapping this servlet onto specific paths. + * + * + * @author Jean-Baptiste Mayer + * + */ +@Singleton +public class AccessDeniedServlet extends HttpServlet { + /** + * + */ + private static final long serialVersionUID = -3239463647917811122L; + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, java.io.IOException { + processRequest(request, response); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + processRequest(request, response); + } + + private void processRequest(HttpServletRequest request, + HttpServletResponse response) { + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + } +} diff --git a/src/main/java/com/gitblit/servlet/RawServlet.java b/src/main/java/com/gitblit/servlet/RawServlet.java index 16d1a977..1d2724bc 100644 --- a/src/main/java/com/gitblit/servlet/RawServlet.java +++ b/src/main/java/com/gitblit/servlet/RawServlet.java @@ -24,6 +24,7 @@ import java.text.MessageFormat; import java.text.ParseException; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -228,23 +229,31 @@ public class RawServlet extends HttpServlet { return; } + Map<String, String> quickContentTypes = new HashMap<>(); + quickContentTypes.put("html", "text/html"); + quickContentTypes.put("htm", "text/html"); + quickContentTypes.put("xml", "application/xml"); + quickContentTypes.put("json", "application/json"); List<PathModel> pathEntries = JGitUtils.getFilesInPath(r, requestedPath, commit); if (pathEntries.isEmpty()) { // requested a specific resource String file = StringUtils.getLastPathElement(requestedPath); try { - String contentType; - List<String> exts = runtimeManager.getSettings().getStrings(Keys.web.prettyPrintExtensions); String ext = StringUtils.getFileExtension(file).toLowerCase(); - if (exts.contains(ext)) { - // extension is a registered text type for pretty printing - contentType = "text/plain"; - } else { - // query Tika for the content type - Tika tika = new Tika(); - contentType = tika.detect(file); + String contentType = quickContentTypes.get(ext); + + if (contentType == null) { + List<String> exts = runtimeManager.getSettings().getStrings(Keys.web.prettyPrintExtensions); + if (exts.contains(ext)) { + // extension is a registered text type for pretty printing + contentType = "text/plain"; + } else { + // query Tika for the content type + Tika tika = new Tika(); + contentType = tika.detect(file); + } } if (contentType == null) { @@ -459,7 +468,7 @@ public class RawServlet extends HttpServlet { served = true; } } finally { - tw.release(); + tw.close(); rw.dispose(); } diff --git a/src/main/java/com/gitblit/tickets/BranchTicketService.java b/src/main/java/com/gitblit/tickets/BranchTicketService.java index 42189bfd..83966939 100644 --- a/src/main/java/com/gitblit/tickets/BranchTicketService.java +++ b/src/main/java/com/gitblit/tickets/BranchTicketService.java @@ -297,7 +297,7 @@ public class BranchTicketService extends ITicketService implements RefsChangedLi log.error("failed to read " + file, e); } finally { if (rw != null) { - rw.release(); + rw.close(); } } return null; @@ -353,7 +353,7 @@ public class BranchTicketService extends ITicketService implements RefsChangedLi } catch (IOException e) { log.error("", e); } finally { - inserter.release(); + inserter.close(); } } @@ -712,7 +712,7 @@ public class BranchTicketService extends ITicketService implements RefsChangedLi } finally { // release the treewalk if (treeWalk != null) { - treeWalk.release(); + treeWalk.close(); } } } finally { @@ -811,7 +811,7 @@ public class BranchTicketService extends ITicketService implements RefsChangedLi // finish the index builder.finish(); } finally { - inserter.release(); + inserter.close(); } return newIndex; } @@ -855,7 +855,7 @@ public class BranchTicketService extends ITicketService implements RefsChangedLi } } finally { if (tw != null) { - tw.release(); + tw.close(); } } return list; @@ -913,10 +913,10 @@ public class BranchTicketService extends ITicketService implements RefsChangedLi rc)); } } finally { - revWalk.release(); + revWalk.close(); } } finally { - odi.release(); + odi.close(); } return success; } diff --git a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java index 9667154f..0ff5c284 100644 --- a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java +++ b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java @@ -23,15 +23,25 @@ import java.net.InetSocketAddress; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.sshd.SshServer; +import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.io.IoServiceFactoryFactory; import org.apache.sshd.common.io.mina.MinaServiceFactoryFactory; import org.apache.sshd.common.io.nio2.Nio2ServiceFactoryFactory; import org.apache.sshd.common.keyprovider.FileKeyPairProvider; import org.apache.sshd.common.util.SecurityUtils; import org.apache.sshd.server.auth.CachingPublicKeyAuthenticator; +import org.apache.sshd.server.UserAuth; +import org.apache.sshd.server.auth.UserAuthKeyboardInteractive; +import org.apache.sshd.server.auth.UserAuthPassword; +import org.apache.sshd.server.auth.UserAuthPublicKey; +import org.apache.sshd.server.auth.gss.GSSAuthenticator; +import org.apache.sshd.server.auth.gss.UserAuthGSS; import org.bouncycastle.openssl.PEMWriter; import org.eclipse.jgit.internal.JGitText; import org.slf4j.Logger; @@ -120,7 +130,49 @@ public class SshDaemon { } else { addr = new InetSocketAddress(bindInterface, port); } - + + //Will do GSS ? + GSSAuthenticator gssAuthenticator = null; + if(settings.getBoolean(Keys.git.sshWithKrb5, false)) { + gssAuthenticator = new SshKrbAuthenticator(gitblit); + String keytabString = settings.getString(Keys.git.sshKrb5Keytab, + ""); + if(! keytabString.isEmpty()) { + gssAuthenticator.setKeytabFile(keytabString); + } + String servicePrincipalName = settings.getString(Keys.git.sshKrb5ServicePrincipalName, + ""); + if(! servicePrincipalName.isEmpty()) { + gssAuthenticator.setServicePrincipalName(servicePrincipalName); + } + } + + //Sort the authenticators for sshd + List<NamedFactory<UserAuth>> userAuthFactories = new ArrayList<>(); + String sshAuthenticatorsOrderString = settings.getString(Keys.git.sshAuthenticatorsOrder, + "password,keyboard-interactive,publickey"); + for(String authenticator: sshAuthenticatorsOrderString.split(",")) { + String authenticatorName = authenticator.trim().toLowerCase(Locale.US); + switch (authenticatorName) { + case "gssapi-with-mic": + if(gssAuthenticator != null) { + userAuthFactories.add(new UserAuthGSS.Factory()); + } + break; + case "publickey": + userAuthFactories.add(new UserAuthPublicKey.Factory()); + break; + case "password": + userAuthFactories.add(new UserAuthPassword.Factory()); + break; + case "keyboard-interactive": + userAuthFactories.add(new UserAuthKeyboardInteractive.Factory()); + break; + default: + log.error("Unknown ssh authenticator: '{}'", authenticatorName); + } + } + // Create the SSH server sshd = SshServer.setUpDefaultServer(); sshd.setPort(addr.getPort()); @@ -128,6 +180,10 @@ public class SshDaemon { sshd.setKeyPairProvider(hostKeyPairProvider); sshd.setPublickeyAuthenticator(new CachingPublicKeyAuthenticator(keyAuthenticator)); sshd.setPasswordAuthenticator(new UsernamePasswordAuthenticator(gitblit)); + if(gssAuthenticator != null) { + sshd.setGSSAuthenticator(gssAuthenticator); + } + sshd.setUserAuthFactories(userAuthFactories); sshd.setSessionFactory(new SshServerSessionFactory()); sshd.setFileSystemFactory(new DisabledFilesystemFactory()); sshd.setTcpipForwardingFilter(new NonForwardingFilter()); diff --git a/src/main/java/com/gitblit/transport/ssh/SshKrbAuthenticator.java b/src/main/java/com/gitblit/transport/ssh/SshKrbAuthenticator.java new file mode 100644 index 00000000..8170c934 --- /dev/null +++ b/src/main/java/com/gitblit/transport/ssh/SshKrbAuthenticator.java @@ -0,0 +1,52 @@ +/* + * Copyright 2015 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.transport.ssh; + +import com.gitblit.manager.IAuthenticationManager; +import com.gitblit.models.UserModel; +import java.util.Locale; +import org.apache.sshd.server.auth.gss.GSSAuthenticator; +import org.apache.sshd.server.session.ServerSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SshKrbAuthenticator extends GSSAuthenticator { + + protected final Logger log = LoggerFactory.getLogger(getClass()); + protected final IAuthenticationManager authManager; + + public SshKrbAuthenticator(IAuthenticationManager authManager) { + this.authManager = authManager; + log.info("registry {}", authManager); + } + + public boolean validateIdentity(ServerSession session, String identity) { + log.info("identify with kerberos {}", identity); + SshDaemonClient client = (SshDaemonClient)session.getAttribute(SshDaemonClient.KEY); + if (client.getUser() != null) { + log.info("{} has already authenticated!", identity); + return true; + } + String username = identity.toLowerCase(Locale.US); + UserModel user = authManager.authenticate(username); + if (user != null) { + client.setUser(user); + return true; + } + log.warn("could not authenticate {} for SSH", username); + return false; + } +} diff --git a/src/main/java/com/gitblit/utils/CompressionUtils.java b/src/main/java/com/gitblit/utils/CompressionUtils.java index d4bfbb34..b06edd22 100644 --- a/src/main/java/com/gitblit/utils/CompressionUtils.java +++ b/src/main/java/com/gitblit/utils/CompressionUtils.java @@ -132,7 +132,7 @@ public class CompressionUtils { } catch (IOException e) {
error(e, repository, "{0} failed to zip files from commit {1}", commit.getName());
} finally {
- tw.release();
+ tw.close();
rw.dispose();
}
return success;
@@ -291,7 +291,7 @@ public class CompressionUtils { } catch (IOException e) {
error(e, repository, "{0} failed to {1} stream files from commit {2}", algorithm, commit.getName());
} finally {
- tw.release();
+ tw.close();
rw.dispose();
}
return success;
diff --git a/src/main/java/com/gitblit/utils/DiffUtils.java b/src/main/java/com/gitblit/utils/DiffUtils.java index 09b8f801..cdebec1b 100644 --- a/src/main/java/com/gitblit/utils/DiffUtils.java +++ b/src/main/java/com/gitblit/utils/DiffUtils.java @@ -229,11 +229,12 @@ public class DiffUtils { * @param commit
* @param comparator
* @param outputType
+ * @param tabLength
* @return the diff
*/
public static DiffOutput getCommitDiff(Repository repository, RevCommit commit,
- DiffComparator comparator, DiffOutputType outputType) {
- return getDiff(repository, null, commit, null, comparator, outputType);
+ DiffComparator comparator, DiffOutputType outputType, int tabLength) {
+ return getDiff(repository, null, commit, null, comparator, outputType, tabLength);
}
/**
@@ -246,11 +247,12 @@ public class DiffUtils { * @param handler
* to use for rendering binary diffs if {@code outputType} is {@link DiffOutputType#HTML HTML}.
* May be {@code null}, resulting in the default behavior.
+ * @param tabLength
* @return the diff
*/
public static DiffOutput getCommitDiff(Repository repository, RevCommit commit,
- DiffComparator comparator, DiffOutputType outputType, BinaryDiffHandler handler) {
- return getDiff(repository, null, commit, null, comparator, outputType, handler);
+ DiffComparator comparator, DiffOutputType outputType, BinaryDiffHandler handler, int tabLength) {
+ return getDiff(repository, null, commit, null, comparator, outputType, handler, tabLength);
}
@@ -263,11 +265,12 @@ public class DiffUtils { * @param path
* @param comparator
* @param outputType
+ * @param tabLength
* @return the diff
*/
public static DiffOutput getDiff(Repository repository, RevCommit commit, String path,
- DiffComparator comparator, DiffOutputType outputType) {
- return getDiff(repository, null, commit, path, comparator, outputType);
+ DiffComparator comparator, DiffOutputType outputType, int tabLength) {
+ return getDiff(repository, null, commit, path, comparator, outputType, tabLength);
}
/**
@@ -282,11 +285,12 @@ public class DiffUtils { * @param handler
* to use for rendering binary diffs if {@code outputType} is {@link DiffOutputType#HTML HTML}.
* May be {@code null}, resulting in the default behavior.
+ * @param tabLength
* @return the diff
*/
public static DiffOutput getDiff(Repository repository, RevCommit commit, String path,
- DiffComparator comparator, DiffOutputType outputType, BinaryDiffHandler handler) {
- return getDiff(repository, null, commit, path, comparator, outputType, handler);
+ DiffComparator comparator, DiffOutputType outputType, BinaryDiffHandler handler, int tabLength) {
+ return getDiff(repository, null, commit, path, comparator, outputType, handler, tabLength);
}
/**
@@ -297,11 +301,13 @@ public class DiffUtils { * @param commit
* @param comparator
* @param outputType
+ * @param tabLength
+ *
* @return the diff
*/
public static DiffOutput getDiff(Repository repository, RevCommit baseCommit, RevCommit commit,
- DiffComparator comparator, DiffOutputType outputType) {
- return getDiff(repository, baseCommit, commit, null, comparator, outputType);
+ DiffComparator comparator, DiffOutputType outputType, int tabLength) {
+ return getDiff(repository, baseCommit, commit, null, comparator, outputType, tabLength);
}
/**
@@ -315,11 +321,12 @@ public class DiffUtils { * @param handler
* to use for rendering binary diffs if {@code outputType} is {@link DiffOutputType#HTML HTML}.
* May be {@code null}, resulting in the default behavior.
+ * @param tabLength
* @return the diff
*/
public static DiffOutput getDiff(Repository repository, RevCommit baseCommit, RevCommit commit,
- DiffComparator comparator, DiffOutputType outputType, BinaryDiffHandler handler) {
- return getDiff(repository, baseCommit, commit, null, comparator, outputType, handler);
+ DiffComparator comparator, DiffOutputType outputType, BinaryDiffHandler handler, int tabLength) {
+ return getDiff(repository, baseCommit, commit, null, comparator, outputType, handler, tabLength);
}
/**
@@ -335,11 +342,12 @@ public class DiffUtils { * or folder. if unspecified, the diff is for the entire commit.
* @param outputType
* @param diffComparator
+ * @param tabLength
* @return the diff
*/
public static DiffOutput getDiff(Repository repository, RevCommit baseCommit, RevCommit commit,
- String path, DiffComparator diffComparator, DiffOutputType outputType) {
- return getDiff(repository, baseCommit, commit, path, diffComparator, outputType, null);
+ String path, DiffComparator diffComparator, DiffOutputType outputType, int tabLength) {
+ return getDiff(repository, baseCommit, commit, path, diffComparator, outputType, null, tabLength);
}
/**
@@ -358,10 +366,11 @@ public class DiffUtils { * @param handler
* to use for rendering binary diffs if {@code outputType} is {@link DiffOutputType#HTML HTML}.
* May be {@code null}, resulting in the default behavior.
+ * @param tabLength
* @return the diff
*/
public static DiffOutput getDiff(Repository repository, RevCommit baseCommit, RevCommit commit, String path,
- DiffComparator comparator, DiffOutputType outputType, final BinaryDiffHandler handler) {
+ DiffComparator comparator, DiffOutputType outputType, final BinaryDiffHandler handler, int tabLength) {
DiffStat stat = null;
String diff = null;
try {
@@ -370,7 +379,7 @@ public class DiffUtils { DiffFormatter df;
switch (outputType) {
case HTML:
- df = new GitBlitDiffFormatter(commit.getName(), path, handler);
+ df = new GitBlitDiffFormatter(commit.getName(), path, handler, tabLength);
break;
case PLAIN:
default:
diff --git a/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java b/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java index 8f0d223a..86b7ca2e 100644 --- a/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java +++ b/src/main/java/com/gitblit/utils/GitBlitDiffFormatter.java @@ -127,6 +127,8 @@ public class GitBlitDiffFormatter extends DiffFormatter { /** If {@link #truncated}, contains all entries skipped. */ private final List<DiffEntry> skipped = new ArrayList<DiffEntry>(); + private int tabLength; + /** * A {@link ResettableByteArrayOutputStream} that intercept the "Binary files differ" message produced * by the super implementation. Unfortunately the super implementation has far too many things private; @@ -162,11 +164,12 @@ public class GitBlitDiffFormatter extends DiffFormatter { } - public GitBlitDiffFormatter(String commitId, String path, BinaryDiffHandler handler) { + public GitBlitDiffFormatter(String commitId, String path, BinaryDiffHandler handler, int tabLength) { super(new DiffOutputStream()); this.os = (DiffOutputStream) getOutputStream(); this.os.setFormatter(this, handler); this.diffStat = new DiffStat(commitId); + this.tabLength = tabLength; // If we have a full commitdiff, install maxima to avoid generating a super-long diff listing that // will only tax the browser too much. maxDiffLinesPerFile = path != null ? -1 : getLimit(DIFF_LIMIT_PER_FILE_KEY, 500, DIFF_LIMIT_PER_FILE); @@ -453,14 +456,14 @@ public class GitBlitDiffFormatter extends DiffFormatter { // Highlight trailing whitespace on deleted/added lines. Matcher matcher = trailingWhitespace.matcher(line); if (matcher.find()) { - StringBuilder result = new StringBuilder(StringUtils.escapeForHtml(line.substring(0, matcher.start()), CONVERT_TABS)); + StringBuilder result = new StringBuilder(StringUtils.escapeForHtml(line.substring(0, matcher.start()), CONVERT_TABS, tabLength)); result.append("<span class='trailingws-").append(prefix == '+' ? "add" : "sub").append("'>"); result.append(StringUtils.escapeForHtml(matcher.group(1), false)); result.append("</span>"); return result.toString(); } } - return StringUtils.escapeForHtml(line, CONVERT_TABS); + return StringUtils.escapeForHtml(line, CONVERT_TABS, tabLength); } /** @@ -473,7 +476,8 @@ public class GitBlitDiffFormatter extends DiffFormatter { String[] lines = html.split("\n"); StringBuilder sb = new StringBuilder(); for (String line : lines) { - if (line.startsWith("index")) { + if (line.startsWith("index") || line.startsWith("similarity") + || line.startsWith("rename from ") || line.startsWith("rename to ")) { // skip index lines } else if (line.startsWith("new file") || line.startsWith("deleted file")) { // skip new file lines @@ -492,7 +496,7 @@ public class GitBlitDiffFormatter extends DiffFormatter { } else { sb.append("<th class='diff-state diff-state-sub'></th><td class=\"diff-cell remove2\">"); } - line = StringUtils.escapeForHtml(line.substring(1), CONVERT_TABS); + line = StringUtils.escapeForHtml(line.substring(1), CONVERT_TABS, tabLength); } sb.append(line); if (gitLinkDiff) { diff --git a/src/main/java/com/gitblit/utils/JGitUtils.java b/src/main/java/com/gitblit/utils/JGitUtils.java index 69084ca1..356a796c 100644 --- a/src/main/java/com/gitblit/utils/JGitUtils.java +++ b/src/main/java/com/gitblit/utils/JGitUtils.java @@ -774,7 +774,7 @@ public class JGitUtils { }
} finally {
rw.dispose();
- tw.release();
+ tw.close();
}
return content;
}
@@ -885,7 +885,7 @@ public class JGitUtils { } catch (IOException e) {
error(e, repository, "{0} failed to get files for commit {1}", commit.getName());
} finally {
- tw.release();
+ tw.close();
}
Collections.sort(list);
return list;
@@ -942,7 +942,7 @@ public class JGitUtils { } catch (IOException e) {
error(e, repository, "{0} failed to get files for commit {1}", commit.getName());
} finally {
- tw.release();
+ tw.close();
}
Collections.sort(list);
return list;
@@ -994,7 +994,7 @@ public class JGitUtils { .getRawMode(0), tw.getObjectId(0).getName(), commit.getId().getName(),
ChangeType.ADD));
}
- tw.release();
+ tw.close();
} else {
RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
DiffStatFormatter df = new DiffStatFormatter(commit.getName());
@@ -1049,7 +1049,7 @@ public class JGitUtils { RevCommit start = rw.parseCommit(startRange);
RevCommit end = rw.parseCommit(endRange);
list.addAll(getFilesInRange(repository, start, end));
- rw.release();
+ rw.close();
} catch (Throwable t) {
error(t, repository, "{0} failed to determine files in range {1}..{2}!", startCommit, endCommit);
}
@@ -1147,7 +1147,7 @@ public class JGitUtils { } catch (IOException e) {
error(e, repository, "{0} failed to get documents for commit {1}", commit.getName());
} finally {
- tw.release();
+ tw.close();
}
Collections.sort(list);
return list;
@@ -2044,7 +2044,7 @@ public class JGitUtils { error(t, repository, "{0} can't find {1} in commit {2}", path, commit.name());
} finally {
rw.dispose();
- tw.release();
+ tw.close();
}
return commitId;
}
@@ -2218,10 +2218,10 @@ public class JGitUtils { success = false;
}
} finally {
- revWalk.release();
+ revWalk.close();
}
} finally {
- odi.release();
+ odi.close();
}
} catch (Throwable t) {
error(t, repository, "Failed to create orphan branch {1} in repository {0}", branchName);
@@ -2412,7 +2412,7 @@ public class JGitUtils { LOGGER.error("Failed to determine canMerge", e);
} finally {
if (revWalk != null) {
- revWalk.release();
+ revWalk.close();
}
}
return MergeStatus.NOT_MERGEABLE;
@@ -2498,14 +2498,14 @@ public class JGitUtils { // return the merge commit id
return new MergeResult(MergeStatus.MERGED, mergeCommitId.getName());
} finally {
- odi.release();
+ odi.close();
}
}
} catch (IOException e) {
LOGGER.error("Failed to merge", e);
} finally {
if (revWalk != null) {
- revWalk.release();
+ revWalk.close();
}
}
return new MergeResult(MergeStatus.FAILED, null);
diff --git a/src/main/java/com/gitblit/utils/RefLogUtils.java b/src/main/java/com/gitblit/utils/RefLogUtils.java index f08c99e7..355c1208 100644 --- a/src/main/java/com/gitblit/utils/RefLogUtils.java +++ b/src/main/java/com/gitblit/utils/RefLogUtils.java @@ -294,10 +294,10 @@ public class RefLogUtils { rc)); } } finally { - revWalk.release(); + revWalk.close(); } } finally { - odi.release(); + odi.close(); } } catch (Throwable t) { error(t, repository, "Failed to commit reflog entry to {0}"); @@ -395,12 +395,12 @@ public class RefLogUtils { } // release the treewalk - treeWalk.release(); + treeWalk.close(); // finish temporary in-core index used for this commit dcBuilder.finish(); } finally { - inserter.release(); + inserter.close(); } return inCoreIndex; } diff --git a/src/main/java/com/gitblit/utils/StringUtils.java b/src/main/java/com/gitblit/utils/StringUtils.java index 087de543..643c52c3 100644 --- a/src/main/java/com/gitblit/utils/StringUtils.java +++ b/src/main/java/com/gitblit/utils/StringUtils.java @@ -79,6 +79,19 @@ public class StringUtils { * @return plain text escaped for html
*/
public static String escapeForHtml(String inStr, boolean changeSpace) {
+ return escapeForHtml(inStr, changeSpace, 4);
+ }
+
+ /**
+ * Prepare text for html presentation. Replace sensitive characters with
+ * html entities.
+ *
+ * @param inStr
+ * @param changeSpace
+ * @param tabLength
+ * @return plain text escaped for html
+ */
+ public static String escapeForHtml(String inStr, boolean changeSpace, int tabLength) {
StringBuilder retStr = new StringBuilder();
int i = 0;
while (i < inStr.length()) {
@@ -93,7 +106,9 @@ public class StringUtils { } else if (changeSpace && inStr.charAt(i) == ' ') {
retStr.append(" ");
} else if (changeSpace && inStr.charAt(i) == '\t') {
- retStr.append(" ");
+ for (int j = 0; j < tabLength; j++) {
+ retStr.append(" ");
+ }
} else {
retStr.append(inStr.charAt(i));
}
diff --git a/src/main/java/com/gitblit/utils/X509Utils.java b/src/main/java/com/gitblit/utils/X509Utils.java index fc0b797d..a2650be4 100644 --- a/src/main/java/com/gitblit/utils/X509Utils.java +++ b/src/main/java/com/gitblit/utils/X509Utils.java @@ -61,6 +61,7 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream;
import javax.crypto.Cipher;
+import javax.naming.ldap.LdapName;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -1117,17 +1118,18 @@ public class X509Utils { }
public static X509Metadata getMetadata(X509Certificate cert) {
- // manually split DN into OID components
- // this is instead of parsing with LdapName which:
- // (1) I don't trust the order of values
- // (2) it filters out values like EMAILADDRESS
- String dn = cert.getSubjectDN().getName();
Map<String, String> oids = new HashMap<String, String>();
- for (String kvp : dn.split(",")) {
- String [] val = kvp.trim().split("=");
- String oid = val[0].toUpperCase().trim();
- String data = val[1].trim();
- oids.put(oid, data);
+ try {
+ String dn = cert.getSubjectDN().getName();
+ LdapName ldapName = new LdapName(dn);
+ for (int i = 0; i < ldapName.size(); i++) {
+ String [] val = ldapName.get(i).trim().split("=", 2);
+ String oid = val[0].toUpperCase().trim();
+ String data = val[1].trim();
+ oids.put(oid, data);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
}
X509Metadata metadata = new X509Metadata(oids.get("CN"), "whocares");
diff --git a/src/main/java/com/gitblit/wicket/pages/BlamePage.java b/src/main/java/com/gitblit/wicket/pages/BlamePage.java index 3c850f29..e45bbbc8 100644 --- a/src/main/java/com/gitblit/wicket/pages/BlamePage.java +++ b/src/main/java/com/gitblit/wicket/pages/BlamePage.java @@ -154,6 +154,7 @@ public class BlamePage extends RepositoryPage { add(new Label("missingBlob").setVisible(false));
+ final int tabLength = app().settings().getInteger(Keys.web.tabLength, 4);
List<AnnotatedLine> lines = DiffUtils.blame(getRepository(), blobPath, objectId);
final Map<?, String> colorMap = initializeColors(activeBlameType, lines);
ListDataProvider<AnnotatedLine> blameDp = new ListDataProvider<AnnotatedLine>(lines);
@@ -212,7 +213,7 @@ public class BlamePage extends RepositoryPage { color = colorMap.get(entry.commitId);
break;
}
- Component data = new Label("data", StringUtils.escapeForHtml(entry.data, true)).setEscapeModelStrings(false);
+ Component data = new Label("data", StringUtils.escapeForHtml(entry.data, true, tabLength)).setEscapeModelStrings(false);
data.add(new SimpleAttributeModifier("style", "background-color: " + color + ";"));
item.add(data);
}
diff --git a/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java b/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java index 187e4600..adf815e6 100644 --- a/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java +++ b/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java @@ -57,7 +57,7 @@ public class BlobDiffPage extends RepositoryPage { RevCommit parent = commit.getParentCount() == 0 ? null : commit.getParent(0);
ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName,
parent.getName(), commit.getName(), imageExtensions);
- diff = DiffUtils.getDiff(r, commit, blobPath, diffComparator, DiffOutputType.HTML, handler).content;
+ diff = DiffUtils.getDiff(r, commit, blobPath, diffComparator, DiffOutputType.HTML, handler, 3).content;
if (handler.getImgDiffCount() > 0) {
addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs
}
@@ -68,7 +68,7 @@ public class BlobDiffPage extends RepositoryPage { RevCommit baseCommit = JGitUtils.getCommit(r, baseObjectId);
ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName,
baseCommit.getName(), commit.getName(), imageExtensions);
- diff = DiffUtils.getDiff(r, baseCommit, commit, blobPath, diffComparator, DiffOutputType.HTML, handler).content;
+ diff = DiffUtils.getDiff(r, baseCommit, commit, blobPath, diffComparator, DiffOutputType.HTML, handler, 3).content;
if (handler.getImgDiffCount() > 0) {
addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs
}
diff --git a/src/main/java/com/gitblit/wicket/pages/BlobPage.java b/src/main/java/com/gitblit/wicket/pages/BlobPage.java index df30e4cc..1ef8f227 100644 --- a/src/main/java/com/gitblit/wicket/pages/BlobPage.java +++ b/src/main/java/com/gitblit/wicket/pages/BlobPage.java @@ -195,7 +195,8 @@ public class BlobPage extends RepositoryPage { } else {
sb.append("<pre class=\"plainprint\">");
}
- lines = StringUtils.escapeForHtml(source, true).split("\n");
+ final int tabLength = app().settings().getInteger(Keys.web.tabLength, 4);
+ lines = StringUtils.escapeForHtml(source, true, tabLength).split("\n");
sb.append("<table width=\"100%\"><tbody>");
diff --git a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java index 95580ed1..3754f3ef 100644 --- a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java +++ b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java @@ -87,8 +87,8 @@ public class CommitDiffPage extends RepositoryPage { final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions); final ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName, parents.isEmpty() ? null : parents.get(0), commit.getName(), imageExtensions); - - final DiffOutput diff = DiffUtils.getCommitDiff(r, commit, diffComparator, DiffOutputType.HTML, handler); + final int tabLength = app().settings().getInteger(Keys.web.tabLength, 4); + final DiffOutput diff = DiffUtils.getCommitDiff(r, commit, diffComparator, DiffOutputType.HTML, handler, tabLength); if (handler.getImgDiffCount() > 0) { addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs } diff --git a/src/main/java/com/gitblit/wicket/pages/ComparePage.java b/src/main/java/com/gitblit/wicket/pages/ComparePage.java index d1710446..7e7ac2f5 100644 --- a/src/main/java/com/gitblit/wicket/pages/ComparePage.java +++ b/src/main/java/com/gitblit/wicket/pages/ComparePage.java @@ -119,9 +119,9 @@ public class ComparePage extends RepositoryPage { final List<String> imageExtensions = app().settings().getStrings(Keys.web.imageExtensions); final ImageDiffHandler handler = new ImageDiffHandler(this, repositoryName, fromCommit.getName(), toCommit.getName(), imageExtensions); - final DiffComparator diffComparator = WicketUtils.getDiffComparator(params); - final DiffOutput diff = DiffUtils.getDiff(r, fromCommit, toCommit, diffComparator, DiffOutputType.HTML, handler); + final int tabLength = app().settings().getInteger(Keys.web.tabLength, 4); + final DiffOutput diff = DiffUtils.getDiff(r, fromCommit, toCommit, diffComparator, DiffOutputType.HTML, handler, tabLength); if (handler.getImgDiffCount() > 0) { addBottomScript("scripts/imgdiff.js"); // Tiny support script for image diffs } diff --git a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html index cf94f3ca..70869a1e 100644 --- a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html +++ b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.html @@ -63,6 +63,13 @@ </ul> </div> + <div class="btn-group"> + <a class="btn dropdown-toggle" data-toggle="dropdown" href="#"> <wicket:message key="gb.repository"></wicket:message>: <span style="font-weight:bold;" wicket:id="currentRepository"></span> <span class="caret"></span></a> + <ul class="dropdown-menu"> + <li wicket:id="repository"><span wicket:id="repositoryLink"></span></li> + </ul> + </div> + <div class="btn-group pull-right"> <div class="pagination pagination-right pagination-small"> <ul> @@ -82,4 +89,4 @@ </wicket:extend> </body> -</html>
\ No newline at end of file +</html> diff --git a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java index 187302f5..591c7fef 100644 --- a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java +++ b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java @@ -17,6 +17,10 @@ package com.gitblit.wicket.pages; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.List; import org.apache.wicket.PageParameters; @@ -27,6 +31,7 @@ import org.apache.wicket.markup.repeater.data.DataView; import org.apache.wicket.markup.repeater.data.ListDataProvider; import com.gitblit.Keys; +import com.gitblit.models.RepositoryModel; import com.gitblit.models.TicketModel; import com.gitblit.models.TicketModel.Status; import com.gitblit.models.UserModel; @@ -52,355 +57,437 @@ import com.gitblit.wicket.panels.UserTitlePanel; */ public class MyTicketsPage extends RootPage { - public MyTicketsPage() { - this(null); - } - - public MyTicketsPage(PageParameters params) { - super(params); - setupPage("", getString("gb.myTickets")); - - UserModel currentUser = GitBlitWebSession.get().getUser(); - if (currentUser == null || UserModel.ANONYMOUS.equals(currentUser)) { - setRedirect(true); - setResponsePage(getApplication().getHomePage()); - return; - } - - final String username = currentUser.getName(); - final String[] statiiParam = (params == null) ? TicketsUI.openStatii : params.getStringArray(Lucene.status.name()); - final String assignedToParam = (params == null) ? "" : params.getString(Lucene.responsible.name(), null); - final String milestoneParam = (params == null) ? "" : params.getString(Lucene.milestone.name(), null); - final String queryParam = (params == null || StringUtils.isEmpty(params.getString("q", null))) ? "watchedby:" + username : params.getString("q", null); - final String searchParam = (params == null) ? "" : params.getString("s", null); - final String sortBy = (params == null) ? "" : Lucene.fromString(params.getString("sort", Lucene.created.name())).name(); - final boolean desc = (params == null) ? true : !"asc".equals(params.getString("direction", "desc")); - - // add the user title panel - add(new UserTitlePanel("userTitlePanel", currentUser, getString("gb.myTickets"))); - - // add search form - add(new TicketSearchForm("ticketSearchForm", null, searchParam, getClass(), params)); - - // standard queries - add(new BookmarkablePageLink<Void>("changesQuery", MyTicketsPage.class, - queryParameters( - Lucene.type.matches(TicketModel.Type.Proposal.name()), - milestoneParam, - statiiParam, - assignedToParam, - sortBy, - desc, - 1))); - - add(new BookmarkablePageLink<Void>("bugsQuery", MyTicketsPage.class, - queryParameters( - Lucene.type.matches(TicketModel.Type.Bug.name()), - milestoneParam, - statiiParam, - assignedToParam, - sortBy, - desc, - 1))); - - add(new BookmarkablePageLink<Void>("enhancementsQuery", MyTicketsPage.class, - queryParameters( - Lucene.type.matches(TicketModel.Type.Enhancement.name()), - milestoneParam, - statiiParam, - assignedToParam, - sortBy, - desc, - 1))); - - add(new BookmarkablePageLink<Void>("tasksQuery", MyTicketsPage.class, - queryParameters( - Lucene.type.matches(TicketModel.Type.Task.name()), - milestoneParam, - statiiParam, - assignedToParam, - sortBy, - desc, - 1))); - - add(new BookmarkablePageLink<Void>("questionsQuery", MyTicketsPage.class, - queryParameters( - Lucene.type.matches(TicketModel.Type.Question.name()), - milestoneParam, - statiiParam, - assignedToParam, - sortBy, - desc, - 1))); - - add(new BookmarkablePageLink<Void>("maintenanceQuery", MyTicketsPage.class, - queryParameters( - Lucene.type.matches(TicketModel.Type.Maintenance.name()), - milestoneParam, - statiiParam, - assignedToParam, - sortBy, - desc, - 1))); - - add(new BookmarkablePageLink<Void>("resetQuery", MyTicketsPage.class, - queryParameters( - null, - milestoneParam, - TicketsUI.openStatii, - null, - null, - true, - 1))); - - add(new Label("userDivider")); - add(new BookmarkablePageLink<Void>("createdQuery", MyTicketsPage.class, - queryParameters( - Lucene.createdby.matches(username), - milestoneParam, - statiiParam, - assignedToParam, - sortBy, - desc, - 1))); - - add(new BookmarkablePageLink<Void>("watchedQuery", MyTicketsPage.class, - queryParameters( - Lucene.watchedby.matches(username), - milestoneParam, - statiiParam, - assignedToParam, - sortBy, - desc, - 1))); - add(new BookmarkablePageLink<Void>("mentionsQuery", MyTicketsPage.class, - queryParameters( - Lucene.mentions.matches(username), - milestoneParam, - statiiParam, - assignedToParam, - sortBy, - desc, - 1))); - add(new BookmarkablePageLink<Void>("responsibleQuery", MyTicketsPage.class, - queryParameters( - Lucene.responsible.matches(username), - milestoneParam, - statiiParam, - assignedToParam, - sortBy, - desc, - 1))); - - // states - if (ArrayUtils.isEmpty(statiiParam)) { - add(new Label("selectedStatii", getString("gb.all"))); - } else { - add(new Label("selectedStatii", StringUtils.flattenStrings(Arrays.asList(statiiParam), ","))); - } - add(new BookmarkablePageLink<Void>("openTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, TicketsUI.openStatii, assignedToParam, sortBy, desc, 1))); - add(new BookmarkablePageLink<Void>("closedTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, TicketsUI.closedStatii, assignedToParam, sortBy, desc, 1))); - add(new BookmarkablePageLink<Void>("allTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, null, assignedToParam, sortBy, desc, 1))); - - // by status - List<Status> statii = new ArrayList<Status>(Arrays.asList(Status.values())); - statii.remove(Status.Closed); - ListDataProvider<Status> resolutionsDp = new ListDataProvider<Status>(statii); - DataView<Status> statiiLinks = new DataView<Status>("statii", resolutionsDp) { - private static final long serialVersionUID = 1L; - - @Override - public void populateItem(final Item<Status> item) { - final Status status = item.getModelObject(); - PageParameters p = queryParameters(queryParam, milestoneParam, new String [] { status.name().toLowerCase() }, assignedToParam, sortBy, desc, 1); - String css = TicketsUI.getStatusClass(status); - item.add(new LinkPanel("statusLink", css, status.toString(), MyTicketsPage.class, p).setRenderBodyOnly(true)); - } - }; - add(statiiLinks); - - List<TicketSort> sortChoices = new ArrayList<TicketSort>(); - sortChoices.add(new TicketSort(getString("gb.sortNewest"), Lucene.created.name(), true)); - sortChoices.add(new TicketSort(getString("gb.sortOldest"), Lucene.created.name(), false)); - sortChoices.add(new TicketSort(getString("gb.sortMostRecentlyUpdated"), Lucene.updated.name(), true)); - sortChoices.add(new TicketSort(getString("gb.sortLeastRecentlyUpdated"), Lucene.updated.name(), false)); - sortChoices.add(new TicketSort(getString("gb.sortMostComments"), Lucene.comments.name(), true)); - sortChoices.add(new TicketSort(getString("gb.sortLeastComments"), Lucene.comments.name(), false)); - sortChoices.add(new TicketSort(getString("gb.sortMostPatchsetRevisions"), Lucene.patchsets.name(), true)); - sortChoices.add(new TicketSort(getString("gb.sortLeastPatchsetRevisions"), Lucene.patchsets.name(), false)); - sortChoices.add(new TicketSort(getString("gb.sortMostVotes"), Lucene.votes.name(), true)); - sortChoices.add(new TicketSort(getString("gb.sortLeastVotes"), Lucene.votes.name(), false)); - sortChoices.add(new TicketSort(getString("gb.sortHighestPriority"), Lucene.priority.name(), true)); - sortChoices.add(new TicketSort(getString("gb.sortLowestPriority"), Lucene.priority.name(), false)); - sortChoices.add(new TicketSort(getString("gb.sortHighestSeverity"), Lucene.severity.name(), true)); - sortChoices.add(new TicketSort(getString("gb.sortLowestSeverity"), Lucene.severity.name(), false)); - - TicketSort currentSort = sortChoices.get(0); - for (TicketSort ts : sortChoices) { - if (ts.sortBy.equals(sortBy) && desc == ts.desc) { - currentSort = ts; - break; - } - } - add(new Label("currentSort", currentSort.name)); - - ListDataProvider<TicketSort> sortChoicesDp = new ListDataProvider<TicketSort>(sortChoices); - DataView<TicketSort> sortMenu = new DataView<TicketSort>("sort", sortChoicesDp) { - private static final long serialVersionUID = 1L; - - @Override - public void populateItem(final Item<TicketSort> item) { - final TicketSort ts = item.getModelObject(); - PageParameters params = queryParameters(queryParam, milestoneParam, statiiParam, assignedToParam, ts.sortBy, ts.desc, 1); - item.add(new LinkPanel("sortLink", null, ts.name, MyTicketsPage.class, params).setRenderBodyOnly(true)); - } - }; - add(sortMenu); - - // Build Query here - QueryBuilder qb = new QueryBuilder(queryParam); - if (!qb.containsField(Lucene.status.name()) && !ArrayUtils.isEmpty(statiiParam)) { - // specify the states - boolean not = false; - QueryBuilder q = new QueryBuilder(); - for (String state : statiiParam) { - if (state.charAt(0) == '!') { - not = true; - q.and(Lucene.status.doesNotMatch(state.substring(1))); - } else { - q.or(Lucene.status.matches(state)); - } - } - if (not) { - qb.and(q.toString()); - } else { - qb.and(q.toSubquery().toString()); - } - } - - final String luceneQuery; - if (qb.containsField(Lucene.createdby.name()) - || qb.containsField(Lucene.responsible.name()) - || qb.containsField(Lucene.watchedby.name())) { - // focused "my tickets" query - luceneQuery = qb.build(); - } else { - // general "my tickets" query - QueryBuilder myQuery = new QueryBuilder(); - myQuery.or(Lucene.createdby.matches(username)); - myQuery.or(Lucene.responsible.matches(username)); - myQuery.or(Lucene.watchedby.matches(username)); - myQuery.and(qb.toSubquery().toString()); - luceneQuery = myQuery.build(); - } - - // paging links - int page = (params != null) ? Math.max(1, WicketUtils.getPage(params)) : 1; - int pageSize = app().settings().getInteger(Keys.tickets.perPage, 25); - - List<QueryResult> results; - if(StringUtils.isEmpty(searchParam)) { - results = app().tickets().queryFor(luceneQuery, page, pageSize, sortBy, desc); - } else { - results = app().tickets().searchFor(null, searchParam, page, pageSize); - } - - int totalResults = results.size() == 0 ? 0 : results.get(0).totalResults; - buildPager(queryParam, milestoneParam, statiiParam, assignedToParam, sortBy, desc, page, pageSize, results.size(), totalResults); - - final boolean showSwatch = app().settings().getBoolean(Keys.web.repositoryListSwatches, true); - add(new TicketListPanel("ticketList", results, showSwatch, true)); - } - - protected PageParameters queryParameters( - String query, - String milestone, - String[] states, - String assignedTo, - String sort, - boolean descending, - int page) { - - PageParameters params = WicketUtils.newRepositoryParameter(""); - if (!StringUtils.isEmpty(query)) { - params.add("q", query); - } - if (!StringUtils.isEmpty(milestone)) { - params.add(Lucene.milestone.name(), milestone); - } - if (!ArrayUtils.isEmpty(states)) { - for (String state : states) { - params.add(Lucene.status.name(), state); - } - } - if (!StringUtils.isEmpty(assignedTo)) { - params.add(Lucene.responsible.name(), assignedTo); - } - if (!StringUtils.isEmpty(sort)) { - params.add("sort", sort); - } - if (!descending) { - params.add("direction", "asc"); - } - if (page > 1) { - params.add("pg", "" + page); - } - return params; - } - - protected void buildPager( - final String query, - final String milestone, - final String [] states, - final String assignedTo, - final String sort, - final boolean desc, - final int page, - int pageSize, - int count, - int total) { - - boolean showNav = total > (2 * pageSize); - boolean allowPrev = page > 1; - boolean allowNext = (pageSize * (page - 1) + count) < total; - add(new BookmarkablePageLink<Void>("prevLink", MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, page - 1)).setEnabled(allowPrev).setVisible(showNav)); - add(new BookmarkablePageLink<Void>("nextLink", MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, page + 1)).setEnabled(allowNext).setVisible(showNav)); - - if (total <= pageSize) { - add(new Label("pageLink").setVisible(false)); - return; - } - - // determine page numbers to display - int pages = count == 0 ? 0 : ((total / pageSize) + (total % pageSize == 0 ? 0 : 1)); - // preferred number of pagelinks - int segments = 5; - if (pages < segments) { - // not enough data for preferred number of page links - segments = pages; - } - int minpage = Math.min(Math.max(1, page - 2), pages - (segments - 1)); - int maxpage = Math.min(pages, minpage + (segments - 1)); - List<Integer> sequence = new ArrayList<Integer>(); - for (int i = minpage; i <= maxpage; i++) { - sequence.add(i); - } - - ListDataProvider<Integer> pagesDp = new ListDataProvider<Integer>(sequence); - DataView<Integer> pagesView = new DataView<Integer>("pageLink", pagesDp) { - private static final long serialVersionUID = 1L; - - @Override - public void populateItem(final Item<Integer> item) { - final Integer i = item.getModelObject(); - LinkPanel link = new LinkPanel("page", null, "" + i, MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, i)); - link.setRenderBodyOnly(true); - if (i == page) { - WicketUtils.setCssClass(item, "active"); - } - item.add(link); - } - }; - add(pagesView); - } + public MyTicketsPage() { + this(null); + } + + public MyTicketsPage(PageParameters params) { + super(params); + setupPage("", getString("gb.myTickets")); + + UserModel currentUser = GitBlitWebSession.get().getUser(); + if (currentUser == null || UserModel.ANONYMOUS.equals(currentUser)) { + setRedirect(true); + setResponsePage(getApplication().getHomePage()); + return; + } + + final String username = currentUser.getName(); + + final String[] statiiParam = (params == null) ? TicketsUI.openStatii : params.getStringArray(Lucene.status.name()); + final String assignedToParam = (params == null) ? "" : params.getString(Lucene.responsible.name(), null); + final String milestoneParam = (params == null) ? "" : params.getString(Lucene.milestone.name(), null); + final String queryParam = (params == null) ? null : params.getString("q", null); + final String searchParam = (params == null) ? "" : params.getString("s", null); + final String sortBy = (params == null) ? "" : Lucene.fromString(params.getString("sort", Lucene.created.name())).name(); + final String repositoryId = (params == null) ? "" : params.getString(Lucene.rid.name(), null); + final boolean desc = (params == null) ? true : !"asc".equals(params.getString("direction", "desc")); + + + // add the user title panel + add(new UserTitlePanel("userTitlePanel", currentUser, getString("gb.myTickets"))); + + // add search form + add(new TicketSearchForm("ticketSearchForm", null, searchParam, getClass(), params)); + + // standard queries + add(new BookmarkablePageLink<Void>("changesQuery", MyTicketsPage.class, + queryParameters( + Lucene.type.matches(TicketModel.Type.Proposal.name()), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + repositoryId, + 1))); + + add(new BookmarkablePageLink<Void>("bugsQuery", MyTicketsPage.class, + queryParameters( + Lucene.type.matches(TicketModel.Type.Bug.name()), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + repositoryId, + 1))); + + add(new BookmarkablePageLink<Void>("enhancementsQuery", MyTicketsPage.class, + queryParameters( + Lucene.type.matches(TicketModel.Type.Enhancement.name()), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + repositoryId, + 1))); + + add(new BookmarkablePageLink<Void>("tasksQuery", MyTicketsPage.class, + queryParameters( + Lucene.type.matches(TicketModel.Type.Task.name()), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + repositoryId, + 1))); + + add(new BookmarkablePageLink<Void>("questionsQuery", MyTicketsPage.class, + queryParameters( + Lucene.type.matches(TicketModel.Type.Question.name()), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + repositoryId, + 1))); + + add(new BookmarkablePageLink<Void>("maintenanceQuery", MyTicketsPage.class, + queryParameters( + Lucene.type.matches(TicketModel.Type.Maintenance.name()), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + repositoryId, + 1))); + + add(new BookmarkablePageLink<Void>("resetQuery", MyTicketsPage.class, + queryParameters( + null, + milestoneParam, + TicketsUI.openStatii, + null, + null, + true, + null, + 1))); + + add(new Label("userDivider")); + add(new BookmarkablePageLink<Void>("createdQuery", MyTicketsPage.class, + queryParameters( + Lucene.createdby.matches(username), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + repositoryId, + 1))); + + add(new BookmarkablePageLink<Void>("watchedQuery", MyTicketsPage.class, + queryParameters( + Lucene.watchedby.matches(username), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + repositoryId, + 1))); + add(new BookmarkablePageLink<Void>("mentionsQuery", MyTicketsPage.class, + queryParameters( + Lucene.mentions.matches(username), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + repositoryId, + 1))); + add(new BookmarkablePageLink<Void>("responsibleQuery", MyTicketsPage.class, + queryParameters( + Lucene.responsible.matches(username), + milestoneParam, + statiiParam, + assignedToParam, + sortBy, + desc, + repositoryId, + 1))); + + // states + if (ArrayUtils.isEmpty(statiiParam)) { + add(new Label("selectedStatii", getString("gb.all"))); + } else { + add(new Label("selectedStatii", StringUtils.flattenStrings(Arrays.asList(statiiParam), ","))); + } + add(new BookmarkablePageLink<Void>("openTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, TicketsUI.openStatii, assignedToParam, sortBy, desc, repositoryId, 1))); + add(new BookmarkablePageLink<Void>("closedTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, TicketsUI.closedStatii, assignedToParam, sortBy, desc, repositoryId, 1))); + add(new BookmarkablePageLink<Void>("allTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, null, assignedToParam, sortBy, desc, repositoryId, 1))); + + // by status + List<Status> statii = new ArrayList<Status>(Arrays.asList(Status.values())); + statii.remove(Status.Closed); + ListDataProvider<Status> resolutionsDp = new ListDataProvider<Status>(statii); + DataView<Status> statiiLinks = new DataView<Status>("statii", resolutionsDp) { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final Item<Status> item) { + final Status status = item.getModelObject(); + PageParameters p = queryParameters(queryParam, milestoneParam, new String [] { status.name().toLowerCase() }, assignedToParam, sortBy, desc, repositoryId, 1); + String css = TicketsUI.getStatusClass(status); + item.add(new LinkPanel("statusLink", css, status.toString(), MyTicketsPage.class, p).setRenderBodyOnly(true)); + } + }; + add(statiiLinks); + + // by sort + List<TicketSort> sortChoices = new ArrayList<TicketSort>(); + sortChoices.add(new TicketSort(getString("gb.sortNewest"), Lucene.created.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortOldest"), Lucene.created.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortMostRecentlyUpdated"), Lucene.updated.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLeastRecentlyUpdated"), Lucene.updated.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortMostComments"), Lucene.comments.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLeastComments"), Lucene.comments.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortMostPatchsetRevisions"), Lucene.patchsets.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLeastPatchsetRevisions"), Lucene.patchsets.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortMostVotes"), Lucene.votes.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLeastVotes"), Lucene.votes.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortHighestPriority"), Lucene.priority.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLowestPriority"), Lucene.priority.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortHighestSeverity"), Lucene.severity.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLowestSeverity"), Lucene.severity.name(), false)); + + TicketSort currentSort = sortChoices.get(0); + for (TicketSort ts : sortChoices) { + if (ts.sortBy.equals(sortBy) && desc == ts.desc) { + currentSort = ts; + break; + } + } + add(new Label("currentSort", currentSort.name)); + + ListDataProvider<TicketSort> sortChoicesDp = new ListDataProvider<TicketSort>(sortChoices); + DataView<TicketSort> sortMenu = new DataView<TicketSort>("sort", sortChoicesDp) { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final Item<TicketSort> item) { + final TicketSort ts = item.getModelObject(); + PageParameters params = queryParameters(queryParam, milestoneParam, statiiParam, assignedToParam, ts.sortBy, ts.desc, repositoryId, 1); + item.add(new LinkPanel("sortLink", null, ts.name, MyTicketsPage.class, params).setRenderBodyOnly(true)); + } + }; + add(sortMenu); + + // by repository + final List<QueryResult> tickets = + query(initializeQueryBuilder(null, username), 1, Integer.MAX_VALUE, sortBy, desc); + final List<RepositoryModel> repositoryChoices = correspondingRepositories(tickets); + Collections.sort(repositoryChoices); + + final RepositoryModel noneChoice = new RepositoryModel(); + noneChoice.name = getString("gb.all"); + repositoryChoices.add(0, noneChoice); + + RepositoryModel currentRepository = repositoryChoices.get(0); + for (RepositoryModel r : repositoryChoices) { + if (r.getRID().equals(repositoryId)) { + currentRepository = r; + break; + } + } + add(new Label("currentRepository", currentRepository.toString())); + + ListDataProvider<RepositoryModel> repositoryChoicesDp = new ListDataProvider<RepositoryModel>(repositoryChoices); + DataView<RepositoryModel> repositoryMenu = new DataView<RepositoryModel>("repository", repositoryChoicesDp) { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final Item<RepositoryModel> item) { + final RepositoryModel r = item.getModelObject(); + String rid = r == noneChoice ? null : r.getRID(); + PageParameters params = queryParameters(queryParam, milestoneParam, statiiParam, assignedToParam, sortBy, desc, rid, 1); + item.add(new LinkPanel("repositoryLink", null, r.toString(), MyTicketsPage.class, params).setRenderBodyOnly(true)); + } + }; + add(repositoryMenu); + + // Update query with filter criteria + final QueryBuilder qb = initializeQueryBuilder(queryParam, username); + + if (!qb.containsField(Lucene.status.name()) && !ArrayUtils.isEmpty(statiiParam)) { + // specify the states + boolean not = false; + QueryBuilder q = new QueryBuilder(); + for (String state : statiiParam) { + if (state.charAt(0) == '!') { + not = true; + q.and(Lucene.status.doesNotMatch(state.substring(1))); + } else { + q.or(Lucene.status.matches(state)); + } + } + + if (not) { + qb.and(q.toString()); + } else { + qb.and(q.toSubquery().toString()); + } + } + + if (noneChoice != currentRepository && !qb.containsField(Lucene.rid.name())) { + QueryBuilder q1 = new QueryBuilder(); + q1.and(Lucene.rid.matches(repositoryId)); + qb.and(q1.toSubquery().toString()); + } + + // paging links + int page = (params != null) ? Math.max(1, WicketUtils.getPage(params)) : 1; + int pageSize = app().settings().getInteger(Keys.tickets.perPage, 25); + + final List<QueryResult> results = + StringUtils.isEmpty(searchParam) ? query(qb, page, pageSize, sortBy, desc) : search(searchParam, page, pageSize); + + int totalResults = results.size() == 0 ? 0 : results.get(0).totalResults; + buildPager(queryParam, milestoneParam, statiiParam, assignedToParam, sortBy, desc, repositoryId, page, pageSize, results.size(), totalResults); + + final boolean showSwatch = app().settings().getBoolean(Keys.web.repositoryListSwatches, true); + add(new TicketListPanel("ticketList", results, showSwatch, true)); + } + + protected PageParameters queryParameters( + String query, + String milestone, + String[] states, + String assignedTo, + String sort, + boolean descending, + String repositoryId, + int page) { + + PageParameters params = WicketUtils.newRepositoryParameter(""); + if (!StringUtils.isEmpty(query)) { + params.add("q", query); + } + if (!StringUtils.isEmpty(milestone)) { + params.add(Lucene.milestone.name(), milestone); + } + if (!ArrayUtils.isEmpty(states)) { + for (String state : states) { + params.add(Lucene.status.name(), state); + } + } + if (!StringUtils.isEmpty(assignedTo)) { + params.add(Lucene.responsible.name(), assignedTo); + } + if (!StringUtils.isEmpty(sort)) { + params.add("sort", sort); + } + if (!descending) { + params.add("direction", "asc"); + } + if (!StringUtils.isEmpty(repositoryId)) { + params.add(Lucene.rid.name(), repositoryId); + } + if (page > 1) { + params.add("pg", "" + page); + } + return params; + } + + protected void buildPager( + final String query, + final String milestone, + final String [] states, + final String assignedTo, + final String sort, + final boolean desc, + final String repositoryId, + final int page, + int pageSize, + int count, + int total) { + + boolean showNav = total > (2 * pageSize); + boolean allowPrev = page > 1; + boolean allowNext = (pageSize * (page - 1) + count) < total; + add(new BookmarkablePageLink<Void>("prevLink", MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, repositoryId, page - 1)).setEnabled(allowPrev).setVisible(showNav)); + add(new BookmarkablePageLink<Void>("nextLink", MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, repositoryId, page + 1)).setEnabled(allowNext).setVisible(showNav)); + + if (total <= pageSize) { + add(new Label("pageLink").setVisible(false)); + return; + } + + // determine page numbers to display + int pages = count == 0 ? 0 : ((total / pageSize) + (total % pageSize == 0 ? 0 : 1)); + // preferred number of pagelinks + int segments = 5; + if (pages < segments) { + // not enough data for preferred number of page links + segments = pages; + } + int minpage = Math.min(Math.max(1, page - 2), pages - (segments - 1)); + int maxpage = Math.min(pages, minpage + (segments - 1)); + List<Integer> sequence = new ArrayList<Integer>(); + for (int i = minpage; i <= maxpage; i++) { + sequence.add(i); + } + + ListDataProvider<Integer> pagesDp = new ListDataProvider<Integer>(sequence); + DataView<Integer> pagesView = new DataView<Integer>("pageLink", pagesDp) { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final Item<Integer> item) { + final Integer i = item.getModelObject(); + LinkPanel link = new LinkPanel("page", null, "" + i, MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, repositoryId, i)); + link.setRenderBodyOnly(true); + if (i == page) { + WicketUtils.setCssClass(item, "active"); + } + item.add(link); + } + }; + add(pagesView); + } + + private QueryBuilder initializeQueryBuilder(String queryparam, String username) { + final QueryBuilder qb = new QueryBuilder(queryparam); + + // focused "my tickets" + if (qb.containsField(Lucene.createdby.name()) + || qb.containsField(Lucene.responsible.name()) + || qb.containsField(Lucene.watchedby.name()) + || qb.containsField(Lucene.mentions.name())) { + + return qb; + } + + // general "my tickets" + return qb.andSubquery() + .or(Lucene.createdby.matches(username)) + .or(Lucene.responsible.matches(username)) + .or(Lucene.watchedby.matches(username)) + .or(Lucene.mentions.matches(username)) + .endSubquery(); + } + + private List<QueryResult> query(QueryBuilder qb, int page, int pageSize, String sortBy, boolean descending) { + return app().tickets().queryFor(qb.build(), page, pageSize, sortBy, descending); + } + + private List<QueryResult> search(String searchParam, int page, int pageSize) { + return app().tickets().searchFor(null, searchParam, page, pageSize); + } + + private List<RepositoryModel> correspondingRepositories(Collection<QueryResult> tickets) { + final HashMap<String, RepositoryModel> result = new HashMap<>(); + for (QueryResult ticket : tickets) { + RepositoryModel repository = app().repositories().getRepositoryModel(ticket.repository); + if (!result.containsKey(repository.getRID())) { + result.put(repository.getRID(), repository); + } + } + + return new ArrayList<>(result.values()); + } } diff --git a/src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.java index b6c23591..d2589e6f 100644 --- a/src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.java +++ b/src/main/java/com/gitblit/wicket/pages/NewRepositoryPage.java @@ -359,14 +359,14 @@ public class NewRepositoryPage extends RootSubPage { }
}
} finally {
- revWalk.release();
+ revWalk.close();
}
} catch (UnsupportedEncodingException e) {
logger().error(null, e);
} catch (IOException e) {
logger().error(null, e);
} finally {
- odi.release();
+ odi.close();
db.close();
}
return success;
diff --git a/src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.java b/src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.java index 8aec9e63..ceca1ec2 100644 --- a/src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.java +++ b/src/main/java/com/gitblit/wicket/pages/ReviewProposalPage.java @@ -76,8 +76,9 @@ public class ReviewProposalPage extends RootSubPage { sb.append(asParam(p, proposal.name, "exclude", ""));
sb.append(asParam(p, proposal.name, "include", ""));
+ final int tabLength = app().settings().getInteger(Keys.web.tabLength, 4);
add(new Label("definition", StringUtils.breakLinesForHtml(StringUtils.escapeForHtml(sb
- .toString().trim(), true))).setEscapeModelStrings(false));
+ .toString().trim(), true, tabLength))).setEscapeModelStrings(false));
List<RepositoryModel> repositories = new ArrayList<RepositoryModel>(
proposal.repositories.values());
diff --git a/src/main/java/com/gitblit/wicket/pages/SessionPage.java b/src/main/java/com/gitblit/wicket/pages/SessionPage.java index 0dda9495..af7f2115 100644 --- a/src/main/java/com/gitblit/wicket/pages/SessionPage.java +++ b/src/main/java/com/gitblit/wicket/pages/SessionPage.java @@ -96,7 +96,12 @@ public abstract class SessionPage extends WebPage { .getAttribute(Constants.AUTHENTICATION_TYPE); // issue 62: fix session fixation vulnerability - session.replaceSession(); + // but only if authentication was done in the container. + // It avoid double change of session, that some authentication method + // don't like + if (AuthenticationType.CONTAINER != authenticationType) { + session.replaceSession(); + } session.setUser(user); request.getSession().setAttribute(Constants.AUTHENTICATION_TYPE, authenticationType); diff --git a/src/main/java/com/gitblit/wicket/panels/HistoryPanel.java b/src/main/java/com/gitblit/wicket/panels/HistoryPanel.java index e1706a09..5233f7c9 100644 --- a/src/main/java/com/gitblit/wicket/panels/HistoryPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/HistoryPanel.java @@ -116,7 +116,7 @@ public class HistoryPanel extends BasePanel { }
} catch (Exception e) {
} finally {
- tw.release();
+ tw.close();
}
}
}
diff --git a/src/site/plugins_extensions.mkd b/src/site/plugins_extensions.mkd index 9e0d1705..82dd30bc 100644 --- a/src/site/plugins_extensions.mkd +++ b/src/site/plugins_extensions.mkd @@ -338,6 +338,16 @@ public class MyRepoLifeCycleListener extends RepositoryLifeCycleListener { } @Override + public void onFork(RepositoryModel origin, RepositoryModel fork) { + log.info("{} forked to {}", origin, fork); + } + + @Override + public void onRename(String oldName, RepositoryModel repo) { + log.info("{} renamed to {}", oldName, repo); + } + + @Override public void onDeletion(RepositoryModel repo) { log.info("Gitblit deleted {}", repo); } diff --git a/src/site/setup_authentication.mkd b/src/site/setup_authentication.mkd index 87b67497..a3bf4451 100644 --- a/src/site/setup_authentication.mkd +++ b/src/site/setup_authentication.mkd @@ -83,10 +83,16 @@ Windows authentication is based on the use of Waffle and JNA. It is known to wo ### PAM Authentication
-PAM authentication is based on the use of libpam4j and JNA. To use this service, your Gitblit server must be installed on a Linux/Unix/MacOSX machine and the user that Gitblit runs-as must have root permissions.
+PAM authentication is based on the use of libpam4j and JNA. To use this service, your Gitblit server must be installed on a Linux/Unix/MacOSX machine.
realm.authenticationProviders = pam
- realm.pam.serviceName = system-auth
+ realm.pam.serviceName = gitblit
+
+Then define a gitblit authentication policy in `/etc/pam.d/gitblit`
+
+ # PAM configuration for the gitblit service
+ # Standard Un*x authentication.
+ @include common-auth
### Htpasswd Authentication
diff --git a/src/test/config/test-gitblit.properties b/src/test/config/test-gitblit.properties index 78e9ab95..398047c1 100644 --- a/src/test/config/test-gitblit.properties +++ b/src/test/config/test-gitblit.properties @@ -9,6 +9,8 @@ git.enableGitServlet = true git.daemonPort = 8300 git.sshPort = 29418 git.sshKeysManager = com.gitblit.transport.ssh.MemoryKeyManager +git.sshWithKrb5 = true +git.sshAuthenticatorsOrder = password, publickey,gssapi-with-mic,invalid groovy.scriptsFolder = src/main/distrib/data/groovy groovy.preReceiveScripts = blockpush groovy.postReceiveScripts = sendmail diff --git a/src/test/java/com/gitblit/tests/AuthenticationManagerTest.java b/src/test/java/com/gitblit/tests/AuthenticationManagerTest.java index 0cdee6cb..d6ca89c6 100644 --- a/src/test/java/com/gitblit/tests/AuthenticationManagerTest.java +++ b/src/test/java/com/gitblit/tests/AuthenticationManagerTest.java @@ -15,15 +15,44 @@ */ package com.gitblit.tests; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import javax.servlet.AsyncContext; +import javax.servlet.DispatcherType; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionContext; +import javax.servlet.http.HttpUpgradeHandler; +import javax.servlet.http.Part; import org.junit.Test; +import com.gitblit.IUserService; +import com.gitblit.Keys; import com.gitblit.manager.AuthenticationManager; import com.gitblit.manager.IAuthenticationManager; -import com.gitblit.manager.IUserManager; +import com.gitblit.manager.IRuntimeManager; import com.gitblit.manager.RuntimeManager; import com.gitblit.manager.UserManager; +import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; import com.gitblit.tests.mock.MemorySettings; import com.gitblit.utils.XssFilter; @@ -35,27 +64,596 @@ import com.gitblit.utils.XssFilter.AllowXssFilter; * @author James Moger * */ +@SuppressWarnings("deprecation") public class AuthenticationManagerTest extends GitblitUnitTest { - IUserManager users; + UserManager users; + + private static final class DummyHttpServletRequest implements HttpServletRequest { + + @Override + public Object getAttribute(String name) { + return null; + } + + @Override + public Enumeration<String> getAttributeNames() { + return null; + } + + @Override + public String getCharacterEncoding() { + return null; + } + + @Override + public void setCharacterEncoding(String env) + throws UnsupportedEncodingException { + } + + @Override + public int getContentLength() { + return 0; + } + + @Override + public long getContentLengthLong() { + return 0; + } + + @Override + public String getContentType() { + return null; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return null; + } + + @Override + public String getParameter(String name) { + return null; + } + + @Override + public Enumeration<String> getParameterNames() { + return null; + } + + @Override + public String[] getParameterValues(String name) { + return null; + } + + @Override + public Map<String, String[]> getParameterMap() { + return null; + } + + @Override + public String getProtocol() { + return null; + } + + @Override + public String getScheme() { + return null; + } + + @Override + public String getServerName() { + return null; + } + + @Override + public int getServerPort() { + return 0; + } + + @Override + public BufferedReader getReader() throws IOException { + return null; + } + + @Override + public String getRemoteAddr() { + return null; + } + + @Override + public String getRemoteHost() { + return null; + } + + @Override + public void setAttribute(String name, Object o) { + } + + @Override + public void removeAttribute(String name) { + } + + @Override + public Locale getLocale() { + return null; + } + + @Override + public Enumeration<Locale> getLocales() { + return null; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + @Override + public String getRealPath(String path) { + return null; + } + + @Override + public int getRemotePort() { + return 0; + } + + @Override + public String getLocalName() { + return null; + } + + @Override + public String getLocalAddr() { + return null; + } + + @Override + public int getLocalPort() { + return 0; + } + + @Override + public ServletContext getServletContext() { + return null; + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, + ServletResponse servletResponse) + throws IllegalStateException { + return null; + } + + @Override + public boolean isAsyncStarted() { + return false; + } + + @Override + public boolean isAsyncSupported() { + return false; + } + + @Override + public AsyncContext getAsyncContext() { + return null; + } + + @Override + public DispatcherType getDispatcherType() { + return null; + } + + @Override + public String getAuthType() { + return null; + } + + @Override + public Cookie[] getCookies() { + return null; + } + + @Override + public long getDateHeader(String name) { + return 0; + } + + @Override + public String getHeader(String name) { + return null; + } + + @Override + public Enumeration<String> getHeaders(String name) { + return null; + } + + @Override + public Enumeration<String> getHeaderNames() { + return null; + } + + @Override + public int getIntHeader(String name) { + return 0; + } + + @Override + public String getMethod() { + return null; + } + + @Override + public String getPathInfo() { + return null; + } + + @Override + public String getPathTranslated() { + return null; + } + + @Override + public String getContextPath() { + return null; + } + + @Override + public String getQueryString() { + return null; + } + + @Override + public String getRemoteUser() { + return null; + } + + @Override + public boolean isUserInRole(String role) { + if(role != null && "admin".equals(role)) { + return true; + } + return false; + } + + @Override + public Principal getUserPrincipal() { + return new Principal(){ + @Override + public String getName() { + return "sunnyjim"; + } + + }; + } + + @Override + public String getRequestedSessionId() { + return null; + } + + @Override + public String getRequestURI() { + return null; + } + + @Override + public StringBuffer getRequestURL() { + return null; + } + + @Override + public String getServletPath() { + return null; + } + + @Override + public HttpSession getSession(boolean create) { + return null; + } + + final Map<String, Object> sessionAttributes = new HashMap<String, Object>(); + @Override + public HttpSession getSession() { + return new HttpSession() { + + @Override + public long getCreationTime() { + return 0; + } + + @Override + public String getId() { + return null; + } + + @Override + public long getLastAccessedTime() { + return 0; + } - MemorySettings getSettings() { - return new MemorySettings(new HashMap<String, Object>()); - } + @Override + public ServletContext getServletContext() { + return null; + } - IAuthenticationManager newAuthenticationManager() { - XssFilter xssFilter = new AllowXssFilter(); - RuntimeManager runtime = new RuntimeManager(getSettings(), xssFilter, GitBlitSuite.BASEFOLDER).start(); - users = new UserManager(runtime, null).start(); - AuthenticationManager auth = new AuthenticationManager(runtime, users).start(); - return auth; - } + @Override + public void setMaxInactiveInterval(int interval) { + } - @Test - public void testAuthenticate() throws Exception { - IAuthenticationManager auth = newAuthenticationManager(); + @Override + public int getMaxInactiveInterval() { + return 0; + } - UserModel user = new UserModel("sunnyjim"); + @Override + public HttpSessionContext getSessionContext() { + return null; + } + + @Override + public Object getAttribute(String name) { + return sessionAttributes.get(name); + } + + @Override + public Object getValue(String name) { + return null; + } + + @Override + public Enumeration<String> getAttributeNames() { + return Collections.enumeration(sessionAttributes.keySet()); + } + + @Override + public String[] getValueNames() { + return null; + } + + @Override + public void setAttribute(String name, + Object value) { + } + + @Override + public void putValue(String name, Object value) { + } + + @Override + public void removeAttribute(String name) { + } + + @Override + public void removeValue(String name) { + } + + @Override + public void invalidate() { + } + + @Override + public boolean isNew() { + return false; + } + + }; + } + + @Override + public String changeSessionId() { + return null; + } + + @Override + public boolean isRequestedSessionIdValid() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromURL() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromUrl() { + return false; + } + + @Override + public boolean authenticate(HttpServletResponse response) + throws IOException, ServletException { + return false; + } + + @Override + public void login(String username, String password) + throws ServletException { + } + + @Override + public void logout() throws ServletException { + } + + @Override + public Collection<Part> getParts() throws IOException, + ServletException { + return null; + } + + @Override + public Part getPart(String name) throws IOException, + ServletException { + return null; + } + + @Override + public <T extends HttpUpgradeHandler> T upgrade( + Class<T> handlerClass) throws IOException, + ServletException { + return null; + } + + } + + HashMap<String, Object> settings = new HashMap<String, Object>(); + + MemorySettings getSettings() { + return new MemorySettings(settings); + } + + IAuthenticationManager newAuthenticationManager() { + XssFilter xssFilter = new AllowXssFilter(); + RuntimeManager runtime = new RuntimeManager(getSettings(), xssFilter, GitBlitSuite.BASEFOLDER).start(); + users = new UserManager(runtime, null).start(); + final Map<String, UserModel> virtualUsers = new HashMap<String, UserModel>(); + users.setUserService(new IUserService() { + + @Override + public void setup(IRuntimeManager runtimeManager) { + } + + @Override + public String getCookie(UserModel model) { + return null; + } + + @Override + public UserModel getUserModel(char[] cookie) { + return null; + } + + @Override + public UserModel getUserModel(String username) { + return virtualUsers.get(username); + } + + @Override + public boolean updateUserModel(UserModel model) { + virtualUsers.put(model.username, model); + return true; + } + + @Override + public boolean updateUserModels(Collection<UserModel> models) { + return false; + } + + @Override + public boolean updateUserModel(String username, UserModel model) { + virtualUsers.put(username, model); + return true; + } + + @Override + public boolean deleteUserModel(UserModel model) { + return false; + } + + @Override + public boolean deleteUser(String username) { + return false; + } + + @Override + public List<String> getAllUsernames() { + return null; + } + + @Override + public List<UserModel> getAllUsers() { + return null; + } + + @Override + public List<String> getAllTeamNames() { + return null; + } + + @Override + public List<TeamModel> getAllTeams() { + return null; + } + + @Override + public List<String> getTeamNamesForRepositoryRole(String role) { + return null; + } + + @Override + public TeamModel getTeamModel(String teamname) { + return null; + } + + @Override + public boolean updateTeamModel(TeamModel model) { + return false; + } + + @Override + public boolean updateTeamModels(Collection<TeamModel> models) { + return false; + } + + @Override + public boolean updateTeamModel(String teamname, TeamModel model) { + return false; + } + + @Override + public boolean deleteTeamModel(TeamModel model) { + return false; + } + + @Override + public boolean deleteTeam(String teamname) { + return false; + } + + @Override + public List<String> getUsernamesForRepositoryRole(String role) { + return null; + } + + @Override + public boolean renameRepositoryRole(String oldRole, + String newRole) { + return false; + } + + @Override + public boolean deleteRepositoryRole(String role) { + return false; + } + + }); + AuthenticationManager auth = new AuthenticationManager(runtime, users).start(); + return auth; + } + + @Test + public void testAuthenticate() throws Exception { + IAuthenticationManager auth = newAuthenticationManager(); + + UserModel user = new UserModel("sunnyjim"); user.password = "password"; users.updateUserModel(user); @@ -65,5 +663,48 @@ public class AuthenticationManagerTest extends GitblitUnitTest { users.updateUserModel(user); assertNull(auth.authenticate(user.username, user.password.toCharArray())); users.deleteUserModel(user); - } + } + + @Test + public void testContenairAuthenticate() throws Exception { + settings.put(Keys.realm.container.autoCreateAccounts, "true"); + settings.put(Keys.realm.container.autoAccounts.displayName, "displayName"); + settings.put(Keys.realm.container.autoAccounts.emailAddress, "emailAddress"); + settings.put(Keys.realm.container.autoAccounts.adminRole, "admin"); + settings.put(Keys.realm.container.autoAccounts.locale, "locale"); + + DummyHttpServletRequest request = new DummyHttpServletRequest(); + request.sessionAttributes.put("displayName", "Sunny Jim"); + request.sessionAttributes.put("emailAddress", "Jim.Sunny@gitblit.com"); + request.sessionAttributes.put("locale", "it"); + + IAuthenticationManager auth = newAuthenticationManager(); + + UserModel user = auth.authenticate(request); + + assertTrue(user.canAdmin); + assertEquals("Sunny Jim", user.displayName); + assertEquals("Jim.Sunny@gitblit.com", user.emailAddress); + assertEquals(Locale.ITALIAN, user.getPreferences().getLocale()); + } + + @Test + public void testContenairAuthenticateEmpty() throws Exception { + settings.put(Keys.realm.container.autoCreateAccounts, "true"); + settings.put(Keys.realm.container.autoAccounts.displayName, "displayName"); + settings.put(Keys.realm.container.autoAccounts.emailAddress, "emailAddress"); + settings.put(Keys.realm.container.autoAccounts.adminRole, "notAdmin"); + + DummyHttpServletRequest request = new DummyHttpServletRequest(); + + IAuthenticationManager auth = newAuthenticationManager(); + + UserModel user = auth.authenticate(request); + + assertFalse(user.canAdmin); + assertEquals("sunnyjim", user.displayName); + assertNull(user.emailAddress); + assertNull(user.getPreferences().getLocale()); + } + } diff --git a/src/test/java/com/gitblit/tests/DiffUtilsTest.java b/src/test/java/com/gitblit/tests/DiffUtilsTest.java index c73e4783..e8e839a5 100644 --- a/src/test/java/com/gitblit/tests/DiffUtilsTest.java +++ b/src/test/java/com/gitblit/tests/DiffUtilsTest.java @@ -41,7 +41,7 @@ public class DiffUtilsTest extends GitblitUnitTest { Repository repository = GitBlitSuite.getHelloworldRepository();
RevCommit commit = JGitUtils.getCommit(repository,
"1d0c2933a4ae69c362f76797d42d6bd182d05176");
- String diff = DiffUtils.getCommitDiff(repository, commit, DiffComparator.SHOW_WHITESPACE, DiffOutputType.PLAIN).content;
+ String diff = DiffUtils.getCommitDiff(repository, commit, DiffComparator.SHOW_WHITESPACE, DiffOutputType.PLAIN, 3).content;
repository.close();
assertTrue(diff != null && diff.length() > 0);
String expected = "- system.out.println(\"Hello World\");\n+ System.out.println(\"Hello World\"";
@@ -55,7 +55,7 @@ public class DiffUtilsTest extends GitblitUnitTest { "8baf6a833b5579384d9b9ceb8a16b5d0ea2ec4ca");
RevCommit commit = JGitUtils.getCommit(repository,
"1d0c2933a4ae69c362f76797d42d6bd182d05176");
- String diff = DiffUtils.getDiff(repository, baseCommit, commit, DiffComparator.SHOW_WHITESPACE, DiffOutputType.PLAIN).content;
+ String diff = DiffUtils.getDiff(repository, baseCommit, commit, DiffComparator.SHOW_WHITESPACE, DiffOutputType.PLAIN, 3).content;
repository.close();
assertTrue(diff != null && diff.length() > 0);
String expected = "- system.out.println(\"Hello World\");\n+ System.out.println(\"Hello World\"";
@@ -67,7 +67,7 @@ public class DiffUtilsTest extends GitblitUnitTest { Repository repository = GitBlitSuite.getHelloworldRepository();
RevCommit commit = JGitUtils.getCommit(repository,
"1d0c2933a4ae69c362f76797d42d6bd182d05176");
- String diff = DiffUtils.getDiff(repository, commit, "java.java", DiffComparator.SHOW_WHITESPACE, DiffOutputType.PLAIN).content;
+ String diff = DiffUtils.getDiff(repository, commit, "java.java", DiffComparator.SHOW_WHITESPACE, DiffOutputType.PLAIN, 3).content;
repository.close();
assertTrue(diff != null && diff.length() > 0);
String expected = "- system.out.println(\"Hello World\");\n+ System.out.println(\"Hello World\"";
diff --git a/src/test/java/com/gitblit/tests/GitBlitSuite.java b/src/test/java/com/gitblit/tests/GitBlitSuite.java index bf6834d3..79a0920d 100644 --- a/src/test/java/com/gitblit/tests/GitBlitSuite.java +++ b/src/test/java/com/gitblit/tests/GitBlitSuite.java @@ -65,7 +65,7 @@ import com.gitblit.utils.JGitUtils; FanoutServiceTest.class, Issue0259Test.class, Issue0271Test.class, HtpasswdAuthenticationTest.class,
ModelUtilsTest.class, JnaUtilsTest.class, LdapSyncServiceTest.class, FileTicketServiceTest.class,
BranchTicketServiceTest.class, RedisTicketServiceTest.class, AuthenticationManagerTest.class,
- SshKeysDispatcherTest.class, UITicketTest.class, PathUtilsTest.class })
+ SshKeysDispatcherTest.class, UITicketTest.class, PathUtilsTest.class, SshKerberosAuthenticationTest.class })
public class GitBlitSuite {
public static final File BASEFOLDER = new File("data");
diff --git a/src/test/java/com/gitblit/tests/JschConfigTestSessionFactory.java b/src/test/java/com/gitblit/tests/JschConfigTestSessionFactory.java index 5d24b401..421f3366 100644 --- a/src/test/java/com/gitblit/tests/JschConfigTestSessionFactory.java +++ b/src/test/java/com/gitblit/tests/JschConfigTestSessionFactory.java @@ -21,6 +21,7 @@ public class JschConfigTestSessionFactory extends JschConfigSessionFactory { @Override
protected void configure(OpenSshConfig.Host host, Session session) {
session.setConfig("StrictHostKeyChecking", "no");
+ session.setConfig("PreferredAuthentications", "password");
}
@Override
diff --git a/src/test/java/com/gitblit/tests/SshKerberosAuthenticationTest.java b/src/test/java/com/gitblit/tests/SshKerberosAuthenticationTest.java new file mode 100644 index 00000000..1bebf61e --- /dev/null +++ b/src/test/java/com/gitblit/tests/SshKerberosAuthenticationTest.java @@ -0,0 +1,76 @@ +/* + * Copyright 2015 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.tests; + +import org.apache.sshd.server.auth.gss.GSSAuthenticator; +import org.apache.sshd.server.session.ServerSession; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import com.gitblit.manager.AuthenticationManager; +import com.gitblit.manager.IRuntimeManager; +import com.gitblit.manager.IUserManager; +import com.gitblit.models.UserModel; +import com.gitblit.transport.ssh.SshDaemonClient; +import com.gitblit.transport.ssh.SshKrbAuthenticator; + +public class SshKerberosAuthenticationTest extends GitblitUnitTest { + + private static class UserModelWrapper { + public UserModel um; + } + + @Test + public void testUserManager() { + IRuntimeManager rm = Mockito.mock(IRuntimeManager.class); + + //Build an UserManager that can build a UserModel + IUserManager im = Mockito.mock(IUserManager.class); + Mockito.doAnswer(new Answer<Object>() { + public Object answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + String user = (String) args[0]; + return new UserModel(user); + } + }).when(im).getUserModel(Mockito.anyString()); + + AuthenticationManager am = new AuthenticationManager(rm, im); + + GSSAuthenticator gssAuthenticator = new SshKrbAuthenticator(am); + + ServerSession session = Mockito.mock(ServerSession.class); + + //Build an SshDaemonClient that can set and get the UserModel + final UserModelWrapper umw = new UserModelWrapper(); + SshDaemonClient client = Mockito.mock(SshDaemonClient.class); + Mockito.when(client.getUser()).thenReturn(umw.um); + Mockito.doAnswer(new Answer<Object>() { + public Object answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + UserModel um = (UserModel) args[0]; + umw.um = um; + return null; + } + }).when(client).setUser(Mockito.any(UserModel.class)); + + Mockito.when(session.getAttribute(SshDaemonClient.KEY)).thenReturn(client); + Assert.assertTrue(gssAuthenticator.validateIdentity(session, "jhappy")); + + } +} diff --git a/src/test/java/com/gitblit/tests/SshUnitTest.java b/src/test/java/com/gitblit/tests/SshUnitTest.java index 43b51b74..3def700d 100644 --- a/src/test/java/com/gitblit/tests/SshUnitTest.java +++ b/src/test/java/com/gitblit/tests/SshUnitTest.java @@ -24,13 +24,18 @@ import java.net.SocketAddress; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PublicKey; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.sshd.ClientChannel; import org.apache.sshd.ClientSession; import org.apache.sshd.SshClient; import org.apache.sshd.client.ServerKeyVerifier; +import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.util.SecurityUtils; +import org.apache.sshd.client.UserAuth; +import org.apache.sshd.client.auth.UserAuthPublicKey; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -102,6 +107,9 @@ public abstract class SshUnitTest extends GitblitUnitTest { return true; } }); + List<NamedFactory<UserAuth>> userAuthFactories = new ArrayList<>(); + userAuthFactories.add(new UserAuthPublicKey.Factory()); + client.setUserAuthFactories(userAuthFactories); client.start(); return client; } diff --git a/src/test/java/com/gitblit/tests/StringUtilsTest.java b/src/test/java/com/gitblit/tests/StringUtilsTest.java index 0fd42aad..7176b88c 100644 --- a/src/test/java/com/gitblit/tests/StringUtilsTest.java +++ b/src/test/java/com/gitblit/tests/StringUtilsTest.java @@ -50,7 +50,7 @@ public class StringUtilsTest extends GitblitUnitTest { public void testEscapeForHtml() throws Exception {
String input = "& < > \" \t";
String outputNoChange = "& < > " \t";
- String outputChange = "& < > " ";
+ String outputChange = "& < > " ";
assertEquals(outputNoChange, StringUtils.escapeForHtml(input, false));
assertEquals(outputChange, StringUtils.escapeForHtml(input, true));
}
|