]> source.dussan.org Git - gitblit.git/commitdiff
Added AccessRestrictionFilter and simplified authentication.
authorJames Moger <james.moger@gitblit.com>
Tue, 14 Jun 2011 20:55:13 +0000 (16:55 -0400)
committerJames Moger <james.moger@gitblit.com>
Tue, 14 Jun 2011 20:55:13 +0000 (16:55 -0400)
Replaced servlet container basic authentication with a custom servlet
filter which performs the same function.  The advantage to this is
that the servlet container is now divorced from the webapp.

The login service (realm) also simplified a great deal and removes its
Jetty dependencies.

Additionally, the basic authorization pop-up will be displayed as
needed based on the repository's access restriction.  This was
necessary for view-restricted repositories with the RSS feature.  Its
also necessary for completely open repositories as before it would
prompt for credentials.

Improved feed syndication feature.

27 files changed:
docs/00_index.mkd
src/com/gitblit/AccessRestrictionFilter.java [new file with mode: 0644]
src/com/gitblit/Constants.java
src/com/gitblit/DownloadZipServlet.java
src/com/gitblit/FileLoginService.java [new file with mode: 0644]
src/com/gitblit/FileSettings.java
src/com/gitblit/GitBlit.java
src/com/gitblit/GitBlitServer.java
src/com/gitblit/GitBlitServlet.java [deleted file]
src/com/gitblit/GitFilter.java [new file with mode: 0644]
src/com/gitblit/JettyLoginService.java [deleted file]
src/com/gitblit/ServletRequestWrapper.java [new file with mode: 0644]
src/com/gitblit/SyndicationFilter.java [new file with mode: 0644]
src/com/gitblit/SyndicationServlet.java
src/com/gitblit/models/UserModel.java
src/com/gitblit/utils/StringUtils.java
src/com/gitblit/utils/SyndicationUtils.java
src/com/gitblit/wicket/GitBlitWebApp.java
src/com/gitblit/wicket/WicketUtils.java
src/com/gitblit/wicket/pages/CommitPage.java
src/com/gitblit/wicket/pages/EditUserPage.java
src/com/gitblit/wicket/pages/LogPage.java
src/com/gitblit/wicket/pages/RepositoryPage.java
src/com/gitblit/wicket/pages/SummaryPage.java
src/com/gitblit/wicket/panels/RepositoriesPanel.html
src/com/gitblit/wicket/panels/RepositoriesPanel.java
tests/com/gitblit/tests/GitBlitSuite.java

index f84773ebba9f6d7741b093cc0814d15d14a2f5bc..bcf41e112855e6d88d97657c829fb47f273193c0 100644 (file)
@@ -61,14 +61,13 @@ sources @ [Github][gitbltsrc]
 - Gitblit may have security holes.  Patches welcome.  :)\r
 \r
 ### Todo List\r
-- Custom BASIC authentication servlet or servlet filter\r
 - Code documentation\r
 - Unit testing\r
 - Update Build.java to JGit 1.0.0, when its released\r
+- WAR solution\r
 \r
 ### Idea List\r
 - Consider clone remote repository feature\r
-- Consider [Apache Shiro](http://shiro.apache.org) for authentication\r
 - Stronger Ticgit read-only integration\r
     - activity/timeline\r
     - query feature with paging support\r
diff --git a/src/com/gitblit/AccessRestrictionFilter.java b/src/com/gitblit/AccessRestrictionFilter.java
new file mode 100644 (file)
index 0000000..3aca103
--- /dev/null
@@ -0,0 +1,240 @@
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit;\r
+\r
+import java.io.IOException;\r
+import java.security.Principal;\r
+import java.text.MessageFormat;\r
+import java.util.Enumeration;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
+import javax.servlet.Filter;\r
+import javax.servlet.FilterChain;\r
+import javax.servlet.FilterConfig;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.ServletRequest;\r
+import javax.servlet.ServletResponse;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+import javax.servlet.http.HttpSession;\r
+\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+/**\r
+ * \r
+ * http://en.wikipedia.org/wiki/Basic_access_authentication\r
+ */\r
+public abstract class AccessRestrictionFilter implements Filter {\r
+\r
+       private static final String BASIC = "Basic";\r
+\r
+       private static final String CHALLENGE = BASIC + " realm=\"" + Constants.NAME + "\"";\r
+\r
+       private static final String SESSION_SECURED = "com.gitblit.secured";\r
+\r
+       protected transient Logger logger;\r
+\r
+       public AccessRestrictionFilter() {\r
+               logger = LoggerFactory.getLogger(getClass());\r
+       }\r
+\r
+       protected abstract String extractRepositoryName(String url);\r
+\r
+       protected abstract String getUrlRequestType(String url);\r
+\r
+       protected abstract boolean requiresAuthentication(RepositoryModel repository);\r
+\r
+       protected abstract boolean canAccess(RepositoryModel repository, UserModel user,\r
+                       String restrictedUrl);\r
+\r
+       @Override\r
+       public void doFilter(final ServletRequest request, final ServletResponse response,\r
+                       final FilterChain chain) throws IOException, ServletException {\r
+\r
+               HttpServletRequest httpRequest = (HttpServletRequest) request;\r
+               HttpServletResponse httpResponse = (HttpServletResponse) response;\r
+\r
+               // Wrap the HttpServletRequest with the AccessRestrictionRequest which\r
+               // overrides the servlet container user principal methods.\r
+               // JGit requires either:\r
+               //\r
+               // 1. servlet container authenticated user\r
+               // 2. http.receivepack = true in each repository's config\r
+               //\r
+               // Gitblit must conditionally authenticate users per-repository so just\r
+               // enabling http.receivepack is insufficient.\r
+\r
+               AccessRestrictionRequest accessRequest = new AccessRestrictionRequest(httpRequest);\r
+\r
+               String url = httpRequest.getRequestURI().substring(httpRequest.getServletPath().length());\r
+               String params = httpRequest.getQueryString();\r
+               if (url.length() > 0 && url.charAt(0) == '/') {\r
+                       url = url.substring(1);\r
+               }\r
+               String fullUrl = url + (StringUtils.isEmpty(params) ? "" : ("?" + params));\r
+\r
+               String repository = extractRepositoryName(url);\r
+\r
+               // Determine if the request URL is restricted\r
+               String fullSuffix = fullUrl.substring(repository.length());\r
+               String urlRequestType = getUrlRequestType(fullSuffix);\r
+\r
+               // Load the repository model\r
+               RepositoryModel model = GitBlit.self().getRepositoryModel(repository);\r
+               if (model == null) {\r
+                       // repository not found. send 404.\r
+                       logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_NOT_FOUND + ")");\r
+                       httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND);\r
+                       return;\r
+               }\r
+\r
+               // BASIC authentication challenge and response processing\r
+               if (!StringUtils.isEmpty(urlRequestType) && requiresAuthentication(model)) {\r
+                       // look for client authorization credentials in header\r
+                       final String authorization = httpRequest.getHeader("Authorization");\r
+                       if (authorization != null && authorization.startsWith(BASIC)) {\r
+                               // Authorization: Basic base64credentials\r
+                               String base64Credentials = authorization.substring(BASIC.length()).trim();\r
+                               String credentials = StringUtils.decodeBase64(base64Credentials);\r
+                               if (GitBlit.isDebugMode()) {\r
+                                       logger.info(MessageFormat.format("AUTH: {0} ({1})", authorization, credentials));\r
+                               }\r
+                               // credentials = username:password\r
+                               final String[] values = credentials.split(":");\r
+\r
+                               if (values.length == 2) {\r
+                                       String username = values[0];\r
+                                       char[] password = values[1].toCharArray();\r
+                                       UserModel user = GitBlit.self().authenticate(username, password);\r
+                                       if (user != null) {\r
+                                               accessRequest.setUser(user);\r
+                                               if (user.canAdmin || canAccess(model, user, urlRequestType)) {\r
+                                                       // authenticated request permitted.\r
+                                                       // pass processing to the restricted servlet.\r
+                                                       newSession(accessRequest, httpResponse);\r
+                                                       logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_CONTINUE + ") authenticated");\r
+                                                       chain.doFilter(accessRequest, httpResponse);\r
+                                                       return;\r
+                                               }\r
+                                               // valid user, but not for requested access. send 403.\r
+                                               if (GitBlit.isDebugMode()) {\r
+                                                       logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_FORBIDDEN\r
+                                                                       + ")");\r
+                                                       logger.info(MessageFormat.format("AUTH: {0} forbidden to access {1}",\r
+                                                                       user.username, url));\r
+                                               }\r
+                                               httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+                                               return;\r
+                                       }\r
+                               }\r
+                               if (GitBlit.isDebugMode()) {\r
+                                       logger.info(MessageFormat\r
+                                                       .format("AUTH: invalid credentials ({0})", credentials));\r
+                               }\r
+                       }\r
+\r
+                       // challenge client to provide credentials. send 401.\r
+                       if (GitBlit.isDebugMode()) {\r
+                               logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_UNAUTHORIZED + ")");\r
+                               logger.info("AUTH: Challenge " + CHALLENGE);\r
+                       }\r
+                       httpResponse.setHeader("WWW-Authenticate", CHALLENGE);\r
+                       httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);\r
+                       return;\r
+               }\r
+\r
+               if (GitBlit.isDebugMode()) {\r
+                       logger.info("ARF: " + fullUrl + " (" + HttpServletResponse.SC_CONTINUE + ") unauthenticated");\r
+               }\r
+               // unauthenticated request permitted.\r
+               // pass processing to the restricted servlet.\r
+               chain.doFilter(accessRequest, httpResponse);\r
+       }\r
+\r
+       /**\r
+        * Taken from Jetty's LoginAuthenticator.renewSessionOnAuthentication()\r
+        */\r
+       protected void newSession(HttpServletRequest request, HttpServletResponse response) {\r
+               HttpSession oldSession = request.getSession(false);\r
+               if (oldSession != null && oldSession.getAttribute(SESSION_SECURED) == null) {\r
+                       synchronized (this) {\r
+                               Map<String, Object> attributes = new HashMap<String, Object>();\r
+                               Enumeration<String> e = oldSession.getAttributeNames();\r
+                               while (e.hasMoreElements()) {\r
+                                       String name = e.nextElement();\r
+                                       attributes.put(name, oldSession.getAttribute(name));\r
+                                       oldSession.removeAttribute(name);\r
+                               }\r
+                               oldSession.invalidate();\r
+\r
+                               HttpSession newSession = request.getSession(true);\r
+                               newSession.setAttribute(SESSION_SECURED, Boolean.TRUE);\r
+                               for (Map.Entry<String, Object> entry : attributes.entrySet()) {\r
+                                       newSession.setAttribute(entry.getKey(), entry.getValue());\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       @Override\r
+       public void init(final FilterConfig config) throws ServletException {\r
+       }\r
+\r
+       @Override\r
+       public void destroy() {\r
+       }\r
+       \r
+       /**\r
+        * Wraps a standard HttpServletRequest and overrides user principal methods.\r
+        */\r
+       public static class AccessRestrictionRequest extends ServletRequestWrapper {\r
+\r
+               private UserModel user;\r
+               \r
+               public AccessRestrictionRequest(HttpServletRequest req) {\r
+                       super(req);\r
+                       user = new UserModel("anonymous");\r
+               }\r
+               \r
+               void setUser(UserModel user) {\r
+                       this.user = user;\r
+               }\r
+\r
+               @Override\r
+               public String getRemoteUser() {\r
+                       return user.username;\r
+               }\r
+\r
+               @Override\r
+               public boolean isUserInRole(String role) {\r
+                       if (role.equals(Constants.ADMIN_ROLE)) {\r
+                               return user.canAdmin;\r
+                       }\r
+                       return user.canAccessRepository(role);\r
+               }\r
+\r
+               @Override\r
+               public Principal getUserPrincipal() {\r
+                       return user;\r
+               }\r
+       }\r
+}
\ No newline at end of file
index 88b13e06b44e8033c69d0754dd93e71cef478c15..68e7b67ed6bfad4549fdbcaa306522a227dabc82 100644 (file)
@@ -38,6 +38,8 @@ public class Constants {
        public static final String ZIP_SERVLET_PATH = "/zip/";\r
        \r
        public static final String SYNDICATION_SERVLET_PATH = "/feed/";\r
+       \r
+       public static final String RESOURCE_PATH = "/com/gitblit/wicket/resources/";\r
 \r
        public static final String BORDER = "***********************************************************";\r
 \r
index 174547462a3a7225fa3f3bbf342961ffa80fba30..3b02cbacc3bcad00f37d836c2edbe5db79952c25 100644 (file)
@@ -41,7 +41,7 @@ public class DownloadZipServlet extends HttpServlet {
        }\r
 \r
        public static String asLink(String baseURL, String repository, String objectId, String path) {\r
-               if (baseURL.charAt(baseURL.length() - 1) == '/') {\r
+               if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {\r
                        baseURL = baseURL.substring(0, baseURL.length() - 1);\r
                }\r
                return baseURL + Constants.ZIP_SERVLET_PATH + "?r=" + repository\r
diff --git a/src/com/gitblit/FileLoginService.java b/src/com/gitblit/FileLoginService.java
new file mode 100644 (file)
index 0000000..b59a776
--- /dev/null
@@ -0,0 +1,373 @@
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit;\r
+\r
+import java.io.File;\r
+import java.io.FileWriter;\r
+import java.io.IOException;\r
+import java.text.MessageFormat;\r
+import java.util.ArrayList;\r
+import java.util.HashSet;\r
+import java.util.List;\r
+import java.util.Properties;\r
+import java.util.Set;\r
+\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+public class FileLoginService extends FileSettings implements ILoginService {\r
+\r
+       private final Logger logger = LoggerFactory.getLogger(FileLoginService.class);\r
+\r
+       public FileLoginService(File realmFile) {\r
+               super(realmFile.getAbsolutePath());\r
+       }\r
+\r
+       @Override\r
+       public UserModel authenticate(String username, char[] password) {\r
+               Properties allUsers = read();\r
+               String userInfo = allUsers.getProperty(username);\r
+               if (StringUtils.isEmpty(userInfo)) {\r
+                       return null;\r
+               }\r
+               UserModel returnedUser = null;\r
+               UserModel user = getUserModel(username);\r
+               if (user.password.startsWith(StringUtils.MD5_TYPE)) {\r
+                       String md5 = StringUtils.MD5_TYPE + StringUtils.getMD5(new String(password));\r
+                       if (user.password.equalsIgnoreCase(md5)) {\r
+                               returnedUser = user;\r
+                       }\r
+               }\r
+               if (user.password.equals(new String(password))) {\r
+                       returnedUser = user;\r
+               }\r
+               return returnedUser;\r
+       }\r
+\r
+       @Override\r
+       public UserModel getUserModel(String username) {\r
+               Properties allUsers = read();\r
+               String userInfo = allUsers.getProperty(username);\r
+               if (userInfo == null) {\r
+                       return null;\r
+               }\r
+               UserModel model = new UserModel(username);\r
+               String[] userValues = userInfo.split(",");\r
+               model.password = userValues[0];\r
+               for (int i = 1; i < userValues.length; i++) {\r
+                       String role = userValues[i];\r
+                       switch (role.charAt(0)) {\r
+                       case '#':\r
+                               // Permissions\r
+                               if (role.equalsIgnoreCase(Constants.ADMIN_ROLE)) {\r
+                                       model.canAdmin = true;\r
+                               }\r
+                               break;\r
+                       default:\r
+                               model.addRepository(role);\r
+                       }\r
+               }\r
+               return model;\r
+       }\r
+\r
+       @Override\r
+       public boolean updateUserModel(UserModel model) {\r
+               return updateUserModel(model.username, model);\r
+       }\r
+\r
+       @Override\r
+       public boolean updateUserModel(String username, UserModel model) {\r
+               try {\r
+                       Properties allUsers = read();\r
+                       ArrayList<String> roles = new ArrayList<String>(model.repositories);\r
+\r
+                       // Permissions\r
+                       if (model.canAdmin) {\r
+                               roles.add(Constants.ADMIN_ROLE);\r
+                       }\r
+\r
+                       StringBuilder sb = new StringBuilder();\r
+                       sb.append(model.password);\r
+                       sb.append(',');\r
+                       for (String role : roles) {\r
+                               sb.append(role);\r
+                               sb.append(',');\r
+                       }\r
+                       // trim trailing comma\r
+                       sb.setLength(sb.length() - 1);\r
+                       allUsers.remove(username);\r
+                       allUsers.put(model.username, sb.toString());\r
+\r
+                       write(allUsers);\r
+                       return true;\r
+               } catch (Throwable t) {\r
+                       logger.error(MessageFormat.format("Failed to update user model {0}!", model.username),\r
+                                       t);\r
+               }\r
+               return false;\r
+       }\r
+\r
+       @Override\r
+       public boolean deleteUserModel(UserModel model) {\r
+               return deleteUser(model.username);\r
+       }\r
+\r
+       @Override\r
+       public boolean deleteUser(String username) {\r
+               try {\r
+                       // Read realm file\r
+                       Properties allUsers = read();\r
+                       allUsers.remove(username);\r
+                       write(allUsers);\r
+                       return true;\r
+               } catch (Throwable t) {\r
+                       logger.error(MessageFormat.format("Failed to delete user {0}!", username), t);\r
+               }\r
+               return false;\r
+       }\r
+\r
+       @Override\r
+       public List<String> getAllUsernames() {\r
+               Properties allUsers = read();\r
+               List<String> list = new ArrayList<String>(allUsers.stringPropertyNames());\r
+               return list;\r
+       }\r
+\r
+       @Override\r
+       public List<String> getUsernamesForRole(String role) {\r
+               List<String> list = new ArrayList<String>();\r
+               try {\r
+                       Properties allUsers = read();\r
+                       for (String username : allUsers.stringPropertyNames()) {\r
+                               String value = allUsers.getProperty(username);\r
+                               String[] values = value.split(",");\r
+                               // skip first value (password)\r
+                               for (int i = 1; i < values.length; i++) {\r
+                                       String r = values[i];\r
+                                       if (r.equalsIgnoreCase(role)) {\r
+                                               list.add(username);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+               } catch (Throwable t) {\r
+                       logger.error(MessageFormat.format("Failed to get usernames for role {0}!", role), t);\r
+               }\r
+               return list;\r
+       }\r
+\r
+       @Override\r
+       public boolean setUsernamesForRole(String role, List<String> usernames) {\r
+               try {\r
+                       Set<String> specifiedUsers = new HashSet<String>(usernames);\r
+                       Set<String> needsAddRole = new HashSet<String>(specifiedUsers);\r
+                       Set<String> needsRemoveRole = new HashSet<String>();\r
+\r
+                       // identify users which require add and remove role\r
+                       Properties allUsers = read();\r
+                       for (String username : allUsers.stringPropertyNames()) {\r
+                               String value = allUsers.getProperty(username);\r
+                               String[] values = value.split(",");\r
+                               // skip first value (password)\r
+                               for (int i = 1; i < values.length; i++) {\r
+                                       String r = values[i];\r
+                                       if (r.equalsIgnoreCase(role)) {\r
+                                               // user has role, check against revised user list\r
+                                               if (specifiedUsers.contains(username)) {\r
+                                                       needsAddRole.remove(username);\r
+                                               } else {\r
+                                                       // remove role from user\r
+                                                       needsRemoveRole.add(username);\r
+                                               }\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       // add roles to users\r
+                       for (String user : needsAddRole) {\r
+                               String userValues = allUsers.getProperty(user);\r
+                               userValues += "," + role;\r
+                               allUsers.put(user, userValues);\r
+                       }\r
+\r
+                       // remove role from user\r
+                       for (String user : needsRemoveRole) {\r
+                               String[] values = allUsers.getProperty(user).split(",");\r
+                               String password = values[0];\r
+                               StringBuilder sb = new StringBuilder();\r
+                               sb.append(password);\r
+                               sb.append(',');\r
+                               List<String> revisedRoles = new ArrayList<String>();\r
+                               // skip first value (password)\r
+                               for (int i = 1; i < values.length; i++) {\r
+                                       String value = values[i];\r
+                                       if (!value.equalsIgnoreCase(role)) {\r
+                                               revisedRoles.add(value);\r
+                                               sb.append(value);\r
+                                               sb.append(',');\r
+                                       }\r
+                               }\r
+                               sb.setLength(sb.length() - 1);\r
+\r
+                               // update properties\r
+                               allUsers.put(user, sb.toString());\r
+                       }\r
+\r
+                       // persist changes\r
+                       write(allUsers);\r
+                       return true;\r
+               } catch (Throwable t) {\r
+                       logger.error(MessageFormat.format("Failed to set usernames for role {0}!", role), t);\r
+               }\r
+               return false;\r
+       }\r
+\r
+       @Override\r
+       public boolean renameRole(String oldRole, String newRole) {\r
+               try {\r
+                       Properties allUsers = read();\r
+                       Set<String> needsRenameRole = new HashSet<String>();\r
+\r
+                       // identify users which require role rename\r
+                       for (String username : allUsers.stringPropertyNames()) {\r
+                               String value = allUsers.getProperty(username);\r
+                               String[] roles = value.split(",");\r
+                               // skip first value (password)\r
+                               for (int i = 1; i < roles.length; i++) {\r
+                                       String r = roles[i];\r
+                                       if (r.equalsIgnoreCase(oldRole)) {\r
+                                               needsRenameRole.remove(username);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       // rename role for identified users\r
+                       for (String user : needsRenameRole) {\r
+                               String userValues = allUsers.getProperty(user);\r
+                               String[] values = userValues.split(",");\r
+                               String password = values[0];\r
+                               StringBuilder sb = new StringBuilder();\r
+                               sb.append(password);\r
+                               sb.append(',');\r
+                               List<String> revisedRoles = new ArrayList<String>();\r
+                               revisedRoles.add(newRole);\r
+                               // skip first value (password)\r
+                               for (int i = 1; i < values.length; i++) {\r
+                                       String value = values[i];\r
+                                       if (!value.equalsIgnoreCase(oldRole)) {\r
+                                               revisedRoles.add(value);\r
+                                               sb.append(value);\r
+                                               sb.append(',');\r
+                                       }\r
+                               }\r
+                               sb.setLength(sb.length() - 1);\r
+\r
+                               // update properties\r
+                               allUsers.put(user, sb.toString());\r
+                       }\r
+\r
+                       // persist changes\r
+                       write(allUsers);\r
+                       return true;\r
+               } catch (Throwable t) {\r
+                       logger.error(\r
+                                       MessageFormat.format("Failed to rename role {0} to {1}!", oldRole, newRole), t);\r
+               }\r
+               return false;\r
+       }\r
+\r
+       @Override\r
+       public boolean deleteRole(String role) {\r
+               try {\r
+                       Properties allUsers = read();\r
+                       Set<String> needsDeleteRole = new HashSet<String>();\r
+\r
+                       // identify users which require role rename\r
+                       for (String username : allUsers.stringPropertyNames()) {\r
+                               String value = allUsers.getProperty(username);\r
+                               String[] roles = value.split(",");\r
+                               // skip first value (password)\r
+                               for (int i = 1; i < roles.length; i++) {\r
+                                       String r = roles[i];\r
+                                       if (r.equalsIgnoreCase(role)) {\r
+                                               needsDeleteRole.remove(username);\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       // delete role for identified users\r
+                       for (String user : needsDeleteRole) {\r
+                               String userValues = allUsers.getProperty(user);\r
+                               String[] values = userValues.split(",");\r
+                               String password = values[0];\r
+                               StringBuilder sb = new StringBuilder();\r
+                               sb.append(password);\r
+                               sb.append(',');\r
+                               List<String> revisedRoles = new ArrayList<String>();\r
+                               // skip first value (password)\r
+                               for (int i = 1; i < values.length; i++) {\r
+                                       String value = values[i];\r
+                                       if (!value.equalsIgnoreCase(role)) {\r
+                                               revisedRoles.add(value);\r
+                                               sb.append(value);\r
+                                               sb.append(',');\r
+                                       }\r
+                               }\r
+                               sb.setLength(sb.length() - 1);\r
+\r
+                               // update properties\r
+                               allUsers.put(user, sb.toString());\r
+                       }\r
+\r
+                       // persist changes\r
+                       write(allUsers);\r
+                       return true;\r
+               } catch (Throwable t) {\r
+                       logger.error(MessageFormat.format("Failed to delete role {0}!", role), t);\r
+               }\r
+               return false;\r
+       }\r
+\r
+       private void write(Properties properties) throws IOException {\r
+               // Update realm file\r
+               File realmFileCopy = new File(propertiesFile.getAbsolutePath() + ".tmp");\r
+               FileWriter writer = new FileWriter(realmFileCopy);\r
+               properties\r
+                               .store(writer,\r
+                                               "# Gitblit realm file format: username=password,\\#permission,repository1,repository2...");\r
+               writer.close();\r
+               if (realmFileCopy.exists() && realmFileCopy.length() > 0) {\r
+                       if (propertiesFile.delete()) {\r
+                               if (!realmFileCopy.renameTo(propertiesFile)) {\r
+                                       throw new IOException(MessageFormat.format("Failed to rename {0} to {1}!",\r
+                                                       realmFileCopy.getAbsolutePath(), propertiesFile.getAbsolutePath()));\r
+                               }\r
+                       } else {\r
+                               throw new IOException(MessageFormat.format("Failed to delete (0)!",\r
+                                               propertiesFile.getAbsolutePath()));\r
+                       }\r
+               } else {\r
+                       throw new IOException(MessageFormat.format("Failed to save {0}!",\r
+                                       realmFileCopy.getAbsolutePath()));\r
+               }\r
+       }\r
+}\r
index b70daa0f0c86b78e9668cd00f48a9827b4002c4c..e213e80f88902a3c6ba0800a09e77f6b4e130bce 100644 (file)
@@ -26,7 +26,7 @@ import java.util.Properties;
  */\r
 public class FileSettings extends IStoredSettings {\r
 \r
-       private final File propertiesFile;\r
+       protected final File propertiesFile;\r
 \r
        private final Properties properties = new Properties();\r
 \r
index 01326230cf6b84a031cbe1e1d0d5f241d2f7ebc7..fa593f9218763681b2b85657b27587b4f3c7f64e 100644 (file)
@@ -20,7 +20,10 @@ import java.io.IOException;
 import java.text.MessageFormat;\r
 import java.util.ArrayList;\r
 import java.util.Collections;\r
+import java.util.HashMap;\r
 import java.util.List;\r
+import java.util.Map;\r
+import java.util.Map.Entry;\r
 \r
 import javax.servlet.ServletContextEvent;\r
 import javax.servlet.ServletContextListener;\r
@@ -87,8 +90,8 @@ public class GitBlit implements ServletContextListener {
                return GITBLIT.storedSettings.getAllKeys(startingWith);\r
        }\r
 \r
-       public boolean isDebugMode() {\r
-               return storedSettings.getBoolean(Keys.web.debugMode, false);\r
+       public static boolean isDebugMode() {\r
+               return GITBLIT.storedSettings.getBoolean(Keys.web.debugMode, false);\r
        }\r
 \r
        public List<String> getOtherCloneUrls(String repositoryName) {\r
@@ -312,6 +315,41 @@ public class GitBlit implements ServletContextListener {
                return false;\r
        }\r
 \r
+       public String processCommitMessage(String repositoryName, String text) {\r
+               String html = StringUtils.breakLinesForHtml(text);\r
+               Map<String, String> map = new HashMap<String, String>();\r
+               // global regex keys\r
+               if (storedSettings.getBoolean(Keys.regex.global, false)) {\r
+                       for (String key : storedSettings.getAllKeys(Keys.regex.global)) {\r
+                               if (!key.equals(Keys.regex.global)) {\r
+                                       String subKey = key.substring(key.lastIndexOf('.') + 1);\r
+                                       map.put(subKey, storedSettings.getString(key, ""));\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // repository-specific regex keys\r
+               List<String> keys = storedSettings.getAllKeys(Keys.regex._ROOT + "."\r
+                               + repositoryName.toLowerCase());\r
+               for (String key : keys) {\r
+                       String subKey = key.substring(key.lastIndexOf('.') + 1);\r
+                       map.put(subKey, storedSettings.getString(key, ""));\r
+               }\r
+\r
+               for (Entry<String, String> entry : map.entrySet()) {\r
+                       String definition = entry.getValue().trim();\r
+                       String[] chunks = definition.split("!!!");\r
+                       if (chunks.length == 2) {\r
+                               html = html.replaceAll(chunks[0], chunks[1]);\r
+                       } else {\r
+                               logger.warn(entry.getKey()\r
+                                               + " improperly formatted.  Use !!! to separate match from replacement: "\r
+                                               + definition);\r
+                       }\r
+               }\r
+               return html;\r
+       }\r
+\r
        public void configureContext(IStoredSettings settings) {\r
                logger.info("Reading configuration from " + settings.toString());\r
                this.storedSettings = settings;\r
@@ -323,7 +361,8 @@ public class GitBlit implements ServletContextListener {
        @Override\r
        public void contextInitialized(ServletContextEvent contextEvent) {\r
                if (storedSettings == null) {\r
-                       // for running gitblit as a traditional webapp in a servlet container\r
+                       // for running gitblit as a traditional webapp in a servlet\r
+                       // container\r
                        WebXmlSettings webxmlSettings = new WebXmlSettings(contextEvent.getServletContext());\r
                        configureContext(webxmlSettings);\r
                }\r
index 2495aeeabc87a1f0a6fc2a2bc41ac2756f1a3bc8..4b6df709741decfd6c621d41b799b1016a955078 100644 (file)
@@ -34,13 +34,7 @@ import org.apache.log4j.ConsoleAppender;
 import org.apache.log4j.PatternLayout;\r
 import org.apache.wicket.protocol.http.ContextParamWebApplicationFactory;\r
 import org.apache.wicket.protocol.http.WicketFilter;\r
-import org.eclipse.jetty.http.security.Constraint;\r
-import org.eclipse.jetty.security.ConstraintMapping;\r
-import org.eclipse.jetty.security.ConstraintSecurityHandler;\r
-import org.eclipse.jetty.security.LoginService;\r
-import org.eclipse.jetty.security.authentication.BasicAuthenticator;\r
 import org.eclipse.jetty.server.Connector;\r
-import org.eclipse.jetty.server.Handler;\r
 import org.eclipse.jetty.server.Server;\r
 import org.eclipse.jetty.server.bio.SocketConnector;\r
 import org.eclipse.jetty.server.nio.SelectChannelConnector;\r
@@ -53,6 +47,7 @@ import org.eclipse.jetty.servlet.FilterMapping;
 import org.eclipse.jetty.servlet.ServletHolder;\r
 import org.eclipse.jetty.util.thread.QueuedThreadPool;\r
 import org.eclipse.jetty.webapp.WebAppContext;\r
+import org.eclipse.jgit.http.server.GitServlet;\r
 import org.slf4j.Logger;\r
 import org.slf4j.LoggerFactory;\r
 \r
@@ -234,77 +229,52 @@ public class GitBlitServer {
                wicketFilter.setInitParameter(ContextParamWebApplicationFactory.APP_CLASS_PARAM,\r
                                GitBlitWebApp.class.getName());\r
                wicketFilter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, wicketPathSpec);\r
-               wicketFilter.setInitParameter(WicketFilter.IGNORE_PATHS_PARAM, "git/");\r
+               wicketFilter.setInitParameter(WicketFilter.IGNORE_PATHS_PARAM, "git/,feed/,zip/");\r
                rootContext.addFilter(wicketFilter, wicketPathSpec, FilterMapping.DEFAULT);\r
 \r
-               // Zip Servlet\r
-               rootContext.addServlet(DownloadZipServlet.class, Constants.ZIP_SERVLET_PATH + "*");\r
-\r
-               // Syndication Servlet\r
-               rootContext.addServlet(SyndicationServlet.class, Constants.SYNDICATION_SERVLET_PATH + "*");\r
-\r
-               // Git Servlet\r
-               ServletHolder gitServlet = null;\r
-               String gitServletPathSpec = Constants.GIT_SERVLET_PATH + "*";\r
+               // JGit Filter and Servlet\r
                if (settings.getBoolean(Keys.git.enableGitServlet, true)) {\r
-                       gitServlet = rootContext.addServlet(GitBlitServlet.class, gitServletPathSpec);\r
-                       gitServlet.setInitParameter("base-path", params.repositoriesFolder);\r
-                       gitServlet.setInitParameter("export-all",\r
+                       String jgitPathSpec = Constants.GIT_SERVLET_PATH + "*";\r
+                       rootContext.addFilter(GitFilter.class, jgitPathSpec, FilterMapping.DEFAULT);\r
+                       ServletHolder jGitServlet = rootContext.addServlet(GitServlet.class, jgitPathSpec);\r
+                       jGitServlet.setInitParameter("base-path", params.repositoriesFolder);\r
+                       jGitServlet.setInitParameter("export-all",\r
                                        settings.getBoolean(Keys.git.exportAll, true) ? "1" : "0");\r
                }\r
 \r
+               // Syndication Filter and Servlet\r
+               String feedPathSpec = Constants.SYNDICATION_SERVLET_PATH + "*";\r
+               rootContext.addFilter(SyndicationFilter.class, feedPathSpec, FilterMapping.DEFAULT);\r
+               rootContext.addServlet(SyndicationServlet.class, feedPathSpec);\r
+\r
+               // Zip Servlet\r
+               rootContext.addServlet(DownloadZipServlet.class, Constants.ZIP_SERVLET_PATH + "*");\r
+\r
                // Login Service\r
-               LoginService loginService = null;\r
                String realmUsers = params.realmFile;\r
-               if (!StringUtils.isEmpty(realmUsers)) {\r
-                       File realmFile = new File(realmUsers);\r
-                       if (realmFile.exists()) {\r
-                               logger.info("Setting up login service from " + realmUsers);\r
-                               JettyLoginService jettyLoginService = new JettyLoginService(realmFile);\r
-                               GitBlit.self().setLoginService(jettyLoginService);\r
-                               loginService = jettyLoginService;\r
-                       }\r
+               if (StringUtils.isEmpty(realmUsers)) {\r
+                       logger.error(MessageFormat.format("PLEASE SPECIFY {0}!!", Keys.realm.realmFile));\r
+                       return;\r
                }\r
-\r
-               // Determine what handler to use\r
-               Handler handler;\r
-               if (gitServlet != null) {\r
-                       if (loginService != null) {\r
-                               // Authenticate Clone/Push\r
-                               logger.info("Setting up authenticated git servlet clone/push access");\r
-\r
-                               Constraint constraint = new Constraint();\r
-                               constraint.setAuthenticate(true);\r
-                               constraint.setRoles(new String[] { "*" });\r
-\r
-                               ConstraintMapping mapping = new ConstraintMapping();\r
-                               mapping.setPathSpec(gitServletPathSpec);\r
-                               mapping.setConstraint(constraint);\r
-\r
-                               ConstraintSecurityHandler security = new ConstraintSecurityHandler();\r
-                               security.addConstraintMapping(mapping);\r
-                               security.setAuthenticator(new BasicAuthenticator());\r
-                               security.setLoginService(loginService);\r
-                               security.setStrict(false);\r
-\r
-                               security.setHandler(rootContext);\r
-\r
-                               handler = security;\r
-                       } else {\r
-                               // Anonymous Pull/Push\r
-                               logger.info("Setting up anonymous git servlet pull/push access");\r
-                               handler = rootContext;\r
+               File realmFile = new File(realmUsers);\r
+               if (!realmFile.exists()) {\r
+                       try {\r
+                               realmFile.createNewFile();\r
+                       } catch (IOException x) {\r
+                               logger.error(MessageFormat.format("COULD NOT CREATE REALM FILE {0}!", realmUsers),\r
+                                               x);\r
+                               return;\r
                        }\r
-               } else {\r
-                       logger.info("Git servlet clone/push disabled");\r
-                       handler = rootContext;\r
                }\r
+               logger.info("Setting up login service from " + realmUsers);\r
+               FileLoginService loginService = new FileLoginService(realmFile);\r
+               GitBlit.self().setLoginService(loginService);\r
 \r
                logger.info("Git repositories folder "\r
                                + new File(params.repositoriesFolder).getAbsolutePath());\r
 \r
                // Set the server's contexts\r
-               server.setHandler(handler);\r
+               server.setHandler(rootContext);\r
 \r
                // Setup the GitBlit context\r
                GitBlit gitblit = GitBlit.self();\r
diff --git a/src/com/gitblit/GitBlitServlet.java b/src/com/gitblit/GitBlitServlet.java
deleted file mode 100644 (file)
index a71012b..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.IOException;\r
-import java.text.MessageFormat;\r
-\r
-import javax.servlet.ServletException;\r
-import javax.servlet.http.HttpServletRequest;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import org.eclipse.jgit.http.server.GitServlet;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.Constants.AccessRestrictionType;\r
-import com.gitblit.models.RepositoryModel;\r
-\r
-public class GitBlitServlet extends GitServlet {\r
-\r
-       private static final long serialVersionUID = 1L;\r
-\r
-       private transient Logger logger = LoggerFactory.getLogger(GitBlitServlet.class);\r
-\r
-       public GitBlitServlet() {\r
-               super();\r
-       }\r
-\r
-       @Override\r
-       protected void service(final HttpServletRequest req, final HttpServletResponse rsp)\r
-                       throws ServletException, IOException {\r
-               // admins have full git access to all repositories\r
-               if (req.isUserInRole(Constants.ADMIN_ROLE)) {\r
-                       // admins can do whatever\r
-                       super.service(req, rsp);\r
-                       return;\r
-               }\r
-\r
-               // try to intercept repository names for authenticated access\r
-               String url = req.getRequestURI().substring(req.getServletPath().length());\r
-               if (url.charAt(0) == '/' && url.length() > 1) {\r
-                       url = url.substring(1);\r
-               }\r
-               int forwardSlash = url.indexOf('/');\r
-               if (forwardSlash > -1) {\r
-                       String repository = url.substring(0, forwardSlash).toLowerCase();\r
-                       String function = url.substring(forwardSlash + 1);\r
-                       String query = req.getQueryString() == null ? "" : req.getQueryString();\r
-                       RepositoryModel model = GitBlit.self().getRepositoryModel(repository);\r
-                       if (model != null) {\r
-                               if (model.isFrozen || model.accessRestriction.atLeast(AccessRestrictionType.PUSH)) {\r
-                                       boolean authorizedUser = req.isUserInRole(repository);\r
-                                       if (function.startsWith("git-receive-pack")\r
-                                                       || (query.indexOf("service=git-receive-pack") > -1)) {\r
-                                               // Push request\r
-                                               if (!model.isFrozen && authorizedUser) {\r
-                                                       // clone-restricted or push-authorized\r
-                                                       super.service(req, rsp);\r
-                                                       return;\r
-                                               } else {\r
-                                                       // user is unauthorized to push to this repository\r
-                                                       logger.warn(MessageFormat.format(\r
-                                                                       "user {0} is not authorized to push to {1}", req\r
-                                                                                       .getUserPrincipal().getName(), repository));\r
-                                                       rsp.sendError(HttpServletResponse.SC_FORBIDDEN, MessageFormat.format(\r
-                                                                       "you are not authorized to push to {0}", repository));\r
-                                                       return;\r
-                                               }\r
-                                       } else if (function.startsWith("git-upload-pack")\r
-                                                       || (query.indexOf("service=git-upload-pack") > -1)) {\r
-                                               // Clone request\r
-                                               boolean cloneRestricted = model.accessRestriction\r
-                                                               .atLeast(AccessRestrictionType.CLONE);\r
-                                               if (!cloneRestricted || (cloneRestricted && authorizedUser)) {\r
-                                                       // push-restricted or clone-authorized\r
-                                                       super.service(req, rsp);\r
-                                                       return;\r
-                                               } else {\r
-                                                       // user is unauthorized to clone this repository\r
-                                                       logger.warn(MessageFormat.format(\r
-                                                                       "user {0} is not authorized to clone {1}", req\r
-                                                                                       .getUserPrincipal().getName(), repository));\r
-                                                       rsp.sendError(HttpServletResponse.SC_FORBIDDEN, MessageFormat.format(\r
-                                                                       "you are not authorized to clone {0}", repository));\r
-                                                       return;\r
-                                               }\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-\r
-               // pass-through to git servlet\r
-               super.service(req, rsp);\r
-       }\r
-}\r
diff --git a/src/com/gitblit/GitFilter.java b/src/com/gitblit/GitFilter.java
new file mode 100644 (file)
index 0000000..5bd7b33
--- /dev/null
@@ -0,0 +1,98 @@
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit;\r
+\r
+import java.text.MessageFormat;\r
+\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+public class GitFilter extends AccessRestrictionFilter {\r
+\r
+       protected final String gitReceivePack = "/git-receive-pack";\r
+\r
+       protected final String gitUploadPack = "/git-upload-pack";\r
+\r
+       protected final String[] suffixes = { gitReceivePack, gitUploadPack, "/info/refs", "/HEAD",\r
+                       "/objects" };\r
+\r
+       @Override\r
+       protected String extractRepositoryName(String url) {\r
+               String repository = url;\r
+               for (String urlSuffix : suffixes) {\r
+                       if (repository.indexOf(urlSuffix) > -1) {\r
+                               repository = repository.substring(0, repository.indexOf(urlSuffix));\r
+                       }\r
+               }\r
+               return repository;\r
+       }\r
+\r
+       @Override\r
+       protected String getUrlRequestType(String suffix) {\r
+               if (!StringUtils.isEmpty(suffix)) {\r
+                       if (suffix.startsWith(gitReceivePack)) {\r
+                               return gitReceivePack;\r
+                       } else if (suffix.startsWith(gitUploadPack)) {\r
+                               return gitUploadPack;\r
+                       } else if (suffix.contains("?service=git-receive-pack")) {\r
+                               return gitReceivePack;\r
+                       } else if (suffix.contains("?service=git-upload-pack")) {\r
+                               return gitUploadPack;\r
+                       }\r
+               }\r
+               return null;\r
+       }\r
+\r
+       @Override\r
+       protected boolean requiresAuthentication(RepositoryModel repository) {\r
+               return repository.accessRestriction.atLeast(AccessRestrictionType.PUSH);\r
+       }\r
+\r
+       @Override\r
+       protected boolean canAccess(RepositoryModel repository, UserModel user, String urlRequestType) {\r
+               if (repository.isFrozen || repository.accessRestriction.atLeast(AccessRestrictionType.PUSH)) {\r
+                       boolean authorizedUser = user.canAccessRepository(repository.name);\r
+                       if (urlRequestType.equals(gitReceivePack)) {\r
+                               // Push request\r
+                               if (!repository.isFrozen && authorizedUser) {\r
+                                       // clone-restricted or push-authorized\r
+                                       return true;\r
+                               } else {\r
+                                       // user is unauthorized to push to this repository\r
+                                       logger.warn(MessageFormat.format("user {0} is not authorized to push to {1}",\r
+                                                       user.username, repository));\r
+                                       return false;\r
+                               }\r
+                       } else if (urlRequestType.equals(gitUploadPack)) {\r
+                               // Clone request\r
+                               boolean cloneRestricted = repository.accessRestriction\r
+                                               .atLeast(AccessRestrictionType.CLONE);\r
+                               if (!cloneRestricted || (cloneRestricted && authorizedUser)) {\r
+                                       // push-restricted or clone-authorized\r
+                                       return true;\r
+                               } else {\r
+                                       // user is unauthorized to clone this repository\r
+                                       logger.warn(MessageFormat.format("user {0} is not authorized to clone {1}",\r
+                                                       user.username, repository));\r
+                                       return false;\r
+                               }\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+}\r
diff --git a/src/com/gitblit/JettyLoginService.java b/src/com/gitblit/JettyLoginService.java
deleted file mode 100644 (file)
index 22f9ce3..0000000
+++ /dev/null
@@ -1,471 +0,0 @@
-/*\r
- * Copyright 2011 gitblit.com.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *     http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.gitblit;\r
-\r
-import java.io.File;\r
-import java.io.FileReader;\r
-import java.io.FileWriter;\r
-import java.io.IOException;\r
-import java.security.Principal;\r
-import java.text.MessageFormat;\r
-import java.util.ArrayList;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Properties;\r
-import java.util.Set;\r
-\r
-import javax.security.auth.Subject;\r
-\r
-import org.eclipse.jetty.http.security.Credential;\r
-import org.eclipse.jetty.security.IdentityService;\r
-import org.eclipse.jetty.security.MappedLoginService;\r
-import org.eclipse.jetty.server.UserIdentity;\r
-import org.eclipse.jetty.util.log.Log;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-import com.gitblit.models.UserModel;\r
-\r
-public class JettyLoginService extends MappedLoginService implements ILoginService {\r
-\r
-       private final Logger logger = LoggerFactory.getLogger(JettyLoginService.class);\r
-\r
-       private final File realmFile;\r
-\r
-       public JettyLoginService(File realmFile) {\r
-               super();\r
-               setName(Constants.NAME);\r
-               this.realmFile = realmFile;\r
-       }\r
-\r
-       @Override\r
-       public UserModel authenticate(String username, char[] password) {\r
-               UserIdentity identity = login(username, new String(password));\r
-               if (identity == null || identity.equals(UserIdentity.UNAUTHENTICATED_IDENTITY)) {\r
-                       return null;\r
-               }\r
-               UserModel user = new UserModel(username);\r
-               user.canAdmin = identity.isUserInRole(Constants.ADMIN_ROLE, null);\r
-\r
-               // Add repositories\r
-               for (Principal principal : identity.getSubject().getPrincipals()) {\r
-                       if (principal instanceof RolePrincipal) {\r
-                               RolePrincipal role = (RolePrincipal) principal;\r
-                               String roleName = role.getName();\r
-                               if (roleName.charAt(0) != '#') {\r
-                                       user.addRepository(roleName);\r
-                               }\r
-                       }\r
-               }\r
-               return user;\r
-       }\r
-\r
-       @Override\r
-       public UserModel getUserModel(String username) {\r
-               UserIdentity identity = _users.get(username);\r
-               if (identity == null) {\r
-                       return null;\r
-               }\r
-               UserModel model = new UserModel(username);\r
-               Subject subject = identity.getSubject();\r
-               for (Principal principal : subject.getPrincipals()) {\r
-                       if (principal instanceof RolePrincipal) {\r
-                               RolePrincipal role = (RolePrincipal) principal;\r
-                               String name = role.getName();\r
-                               switch (name.charAt(0)) {\r
-                               case '#':\r
-                                       // Permissions\r
-                                       if (name.equalsIgnoreCase(Constants.ADMIN_ROLE)) {\r
-                                               model.canAdmin = true;\r
-                                       }\r
-                                       break;\r
-                               default:\r
-                                       model.addRepository(name);\r
-                               }\r
-                       }\r
-               }\r
-               // Retrieve the password from the realm file.\r
-               // Stupid, I know, but the password is buried within protected inner\r
-               // classes in private variables. Too much work to reflectively retrieve.\r
-               try {\r
-                       Properties allUsers = readRealmFile();\r
-                       String value = allUsers.getProperty(username);\r
-                       String password = value.split(",")[0];\r
-                       model.password = password;\r
-               } catch (Throwable t) {\r
-                       logger.error(MessageFormat.format("Failed to read password for user {0}!", username), t);\r
-               }\r
-               return model;\r
-       }\r
-\r
-       @Override\r
-       public boolean updateUserModel(UserModel model) {\r
-               return updateUserModel(model.username, model);\r
-       }\r
-\r
-       @Override\r
-       public boolean updateUserModel(String username, UserModel model) {\r
-               try {\r
-                       Properties allUsers = readRealmFile();\r
-                       ArrayList<String> roles = new ArrayList<String>(model.repositories);\r
-\r
-                       // Permissions\r
-                       if (model.canAdmin) {\r
-                               roles.add(Constants.ADMIN_ROLE);\r
-                       }\r
-\r
-                       StringBuilder sb = new StringBuilder();\r
-                       sb.append(model.password);\r
-                       sb.append(',');\r
-                       for (String role : roles) {\r
-                               sb.append(role);\r
-                               sb.append(',');\r
-                       }\r
-                       // trim trailing comma\r
-                       sb.setLength(sb.length() - 1);\r
-                       allUsers.remove(username);\r
-                       allUsers.put(model.username, sb.toString());\r
-\r
-                       writeRealmFile(allUsers);\r
-\r
-                       // Update login service\r
-                       removeUser(username);\r
-                       putUser(model.username, Credential.getCredential(model.password),\r
-                                       roles.toArray(new String[0]));\r
-                       return true;\r
-               } catch (Throwable t) {\r
-                       logger.error(MessageFormat.format("Failed to update user model {0}!", model.username),\r
-                                       t);\r
-               }\r
-               return false;\r
-       }\r
-\r
-       @Override\r
-       public boolean deleteUserModel(UserModel model) {\r
-               return deleteUser(model.username);\r
-       }\r
-\r
-       @Override\r
-       public boolean deleteUser(String username) {\r
-               try {\r
-                       // Read realm file\r
-                       Properties allUsers = readRealmFile();\r
-                       allUsers.remove(username);\r
-                       writeRealmFile(allUsers);\r
-\r
-                       // Drop user from map\r
-                       removeUser(username);\r
-                       return true;\r
-               } catch (Throwable t) {\r
-                       logger.error(MessageFormat.format("Failed to delete user {0}!", username), t);\r
-               }\r
-               return false;\r
-       }\r
-\r
-       @Override\r
-       public List<String> getAllUsernames() {\r
-               List<String> list = new ArrayList<String>();\r
-               list.addAll(_users.keySet());\r
-               return list;\r
-       }\r
-\r
-       @Override\r
-       public List<String> getUsernamesForRole(String role) {\r
-               List<String> list = new ArrayList<String>();\r
-               try {\r
-                       Properties allUsers = readRealmFile();\r
-                       for (String username : allUsers.stringPropertyNames()) {\r
-                               String value = allUsers.getProperty(username);\r
-                               String[] values = value.split(",");\r
-                               // skip first value (password)\r
-                               for (int i = 1; i < values.length; i++) {\r
-                                       String r = values[i];\r
-                                       if (r.equalsIgnoreCase(role)) {\r
-                                               list.add(username);\r
-                                               break;\r
-                                       }\r
-                               }\r
-                       }\r
-               } catch (Throwable t) {\r
-                       logger.error(MessageFormat.format("Failed to get usernames for role {0}!", role), t);\r
-               }\r
-               return list;\r
-       }\r
-\r
-       @Override\r
-       public boolean setUsernamesForRole(String role, List<String> usernames) {\r
-               try {\r
-                       Set<String> specifiedUsers = new HashSet<String>(usernames);\r
-                       Set<String> needsAddRole = new HashSet<String>(specifiedUsers);\r
-                       Set<String> needsRemoveRole = new HashSet<String>();\r
-\r
-                       // identify users which require add and remove role\r
-                       Properties allUsers = readRealmFile();\r
-                       for (String username : allUsers.stringPropertyNames()) {\r
-                               String value = allUsers.getProperty(username);\r
-                               String[] values = value.split(",");\r
-                               // skip first value (password)\r
-                               for (int i = 1; i < values.length; i++) {\r
-                                       String r = values[i];\r
-                                       if (r.equalsIgnoreCase(role)) {\r
-                                               // user has role, check against revised user list\r
-                                               if (specifiedUsers.contains(username)) {\r
-                                                       needsAddRole.remove(username);\r
-                                               } else {\r
-                                                       // remove role from user\r
-                                                       needsRemoveRole.add(username);\r
-                                               }\r
-                                               break;\r
-                                       }\r
-                               }\r
-                       }\r
-\r
-                       // add roles to users\r
-                       for (String user : needsAddRole) {\r
-                               String userValues = allUsers.getProperty(user);\r
-                               userValues += "," + role;\r
-                               allUsers.put(user, userValues);\r
-                               String[] values = userValues.split(",");\r
-                               String password = values[0];\r
-                               String[] roles = new String[values.length - 1];\r
-                               System.arraycopy(values, 1, roles, 0, values.length - 1);\r
-                               putUser(user, Credential.getCredential(password), roles);\r
-                       }\r
-\r
-                       // remove role from user\r
-                       for (String user : needsRemoveRole) {\r
-                               String[] values = allUsers.getProperty(user).split(",");\r
-                               String password = values[0];\r
-                               StringBuilder sb = new StringBuilder();\r
-                               sb.append(password);\r
-                               sb.append(',');\r
-                               List<String> revisedRoles = new ArrayList<String>();\r
-                               // skip first value (password)\r
-                               for (int i = 1; i < values.length; i++) {\r
-                                       String value = values[i];\r
-                                       if (!value.equalsIgnoreCase(role)) {\r
-                                               revisedRoles.add(value);\r
-                                               sb.append(value);\r
-                                               sb.append(',');\r
-                                       }\r
-                               }\r
-                               sb.setLength(sb.length() - 1);\r
-\r
-                               // update properties\r
-                               allUsers.put(user, sb.toString());\r
-\r
-                               // update memory\r
-                               putUser(user, Credential.getCredential(password),\r
-                                               revisedRoles.toArray(new String[0]));\r
-                       }\r
-\r
-                       // persist changes\r
-                       writeRealmFile(allUsers);\r
-                       return true;\r
-               } catch (Throwable t) {\r
-                       logger.error(MessageFormat.format("Failed to set usernames for role {0}!", role), t);\r
-               }\r
-               return false;\r
-       }\r
-\r
-       @Override\r
-       public boolean renameRole(String oldRole, String newRole) {\r
-               try {\r
-                       Properties allUsers = readRealmFile();\r
-                       Set<String> needsRenameRole = new HashSet<String>();\r
-\r
-                       // identify users which require role rename\r
-                       for (String username : allUsers.stringPropertyNames()) {\r
-                               String value = allUsers.getProperty(username);\r
-                               String[] roles = value.split(",");\r
-                               // skip first value (password)\r
-                               for (int i = 1; i < roles.length; i++) {\r
-                                       String r = roles[i];\r
-                                       if (r.equalsIgnoreCase(oldRole)) {\r
-                                               needsRenameRole.remove(username);\r
-                                               break;\r
-                                       }\r
-                               }\r
-                       }\r
-\r
-                       // rename role for identified users\r
-                       for (String user : needsRenameRole) {\r
-                               String userValues = allUsers.getProperty(user);\r
-                               String[] values = userValues.split(",");\r
-                               String password = values[0];\r
-                               StringBuilder sb = new StringBuilder();\r
-                               sb.append(password);\r
-                               sb.append(',');\r
-                               List<String> revisedRoles = new ArrayList<String>();\r
-                               revisedRoles.add(newRole);\r
-                               // skip first value (password)\r
-                               for (int i = 1; i < values.length; i++) {\r
-                                       String value = values[i];\r
-                                       if (!value.equalsIgnoreCase(oldRole)) {\r
-                                               revisedRoles.add(value);\r
-                                               sb.append(value);\r
-                                               sb.append(',');\r
-                                       }\r
-                               }\r
-                               sb.setLength(sb.length() - 1);\r
-\r
-                               // update properties\r
-                               allUsers.put(user, sb.toString());\r
-\r
-                               // update memory\r
-                               putUser(user, Credential.getCredential(password),\r
-                                               revisedRoles.toArray(new String[0]));\r
-                       }\r
-\r
-                       // persist changes\r
-                       writeRealmFile(allUsers);\r
-                       return true;\r
-               } catch (Throwable t) {\r
-                       logger.error(\r
-                                       MessageFormat.format("Failed to rename role {0} to {1}!", oldRole, newRole), t);\r
-               }\r
-               return false;\r
-       }\r
-\r
-       @Override\r
-       public boolean deleteRole(String role) {\r
-               try {\r
-                       Properties allUsers = readRealmFile();\r
-                       Set<String> needsDeleteRole = new HashSet<String>();\r
-\r
-                       // identify users which require role rename\r
-                       for (String username : allUsers.stringPropertyNames()) {\r
-                               String value = allUsers.getProperty(username);\r
-                               String[] roles = value.split(",");\r
-                               // skip first value (password)\r
-                               for (int i = 1; i < roles.length; i++) {\r
-                                       String r = roles[i];\r
-                                       if (r.equalsIgnoreCase(role)) {\r
-                                               needsDeleteRole.remove(username);\r
-                                               break;\r
-                                       }\r
-                               }\r
-                       }\r
-\r
-                       // delete role for identified users\r
-                       for (String user : needsDeleteRole) {\r
-                               String userValues = allUsers.getProperty(user);\r
-                               String[] values = userValues.split(",");\r
-                               String password = values[0];\r
-                               StringBuilder sb = new StringBuilder();\r
-                               sb.append(password);\r
-                               sb.append(',');\r
-                               List<String> revisedRoles = new ArrayList<String>();\r
-                               // skip first value (password)\r
-                               for (int i = 1; i < values.length; i++) {\r
-                                       String value = values[i];\r
-                                       if (!value.equalsIgnoreCase(role)) {\r
-                                               revisedRoles.add(value);\r
-                                               sb.append(value);\r
-                                               sb.append(',');\r
-                                       }\r
-                               }\r
-                               sb.setLength(sb.length() - 1);\r
-\r
-                               // update properties\r
-                               allUsers.put(user, sb.toString());\r
-\r
-                               // update memory\r
-                               putUser(user, Credential.getCredential(password),\r
-                                               revisedRoles.toArray(new String[0]));\r
-                       }\r
-\r
-                       // persist changes\r
-                       writeRealmFile(allUsers);\r
-                       return true;\r
-               } catch (Throwable t) {\r
-                       logger.error(MessageFormat.format("Failed to delete role {0}!", role), t);\r
-               }\r
-               return false;\r
-       }\r
-\r
-       private Properties readRealmFile() throws IOException {\r
-               Properties allUsers = new Properties();\r
-               FileReader reader = new FileReader(realmFile);\r
-               allUsers.load(reader);\r
-               reader.close();\r
-               return allUsers;\r
-       }\r
-\r
-       private void writeRealmFile(Properties properties) throws IOException {\r
-               // Update realm file\r
-               File realmFileCopy = new File(realmFile.getAbsolutePath() + ".tmp");\r
-               FileWriter writer = new FileWriter(realmFileCopy);\r
-               properties\r
-                               .store(writer,\r
-                                               "# Gitblit realm file format: username=password,\\#permission,repository1,repository2...");\r
-               writer.close();\r
-               if (realmFileCopy.exists() && realmFileCopy.length() > 0) {\r
-                       if (realmFile.delete()) {\r
-                               if (!realmFileCopy.renameTo(realmFile)) {\r
-                                       throw new IOException(MessageFormat.format("Failed to rename {0} to {1}!",\r
-                                                       realmFileCopy.getAbsolutePath(), realmFile.getAbsolutePath()));\r
-                               }\r
-                       } else {\r
-                               throw new IOException(MessageFormat.format("Failed to delete (0)!",\r
-                                               realmFile.getAbsolutePath()));\r
-                       }\r
-               } else {\r
-                       throw new IOException(MessageFormat.format("Failed to save {0}!",\r
-                                       realmFileCopy.getAbsolutePath()));\r
-               }\r
-       }\r
-\r
-       /* ------------------------------------------------------------ */\r
-       @Override\r
-       public void loadUsers() throws IOException {\r
-               if (realmFile == null) {\r
-                       return;\r
-               }\r
-\r
-               if (Log.isDebugEnabled()) {\r
-                       Log.debug("Load " + this + " from " + realmFile);\r
-               }\r
-               Properties allUsers = readRealmFile();\r
-\r
-               // Map Users\r
-               for (Map.Entry<Object, Object> entry : allUsers.entrySet()) {\r
-                       String username = ((String) entry.getKey()).trim();\r
-                       String credentials = ((String) entry.getValue()).trim();\r
-                       String roles = null;\r
-                       int c = credentials.indexOf(',');\r
-                       if (c > 0) {\r
-                               roles = credentials.substring(c + 1).trim();\r
-                               credentials = credentials.substring(0, c).trim();\r
-                       }\r
-\r
-                       if (username != null && username.length() > 0 && credentials != null\r
-                                       && credentials.length() > 0) {\r
-                               String[] roleArray = IdentityService.NO_ROLES;\r
-                               if (roles != null && roles.length() > 0) {\r
-                                       roleArray = roles.split(",");\r
-                               }\r
-                               putUser(username, Credential.getCredential(credentials), roleArray);\r
-                       }\r
-               }\r
-       }\r
-\r
-       @Override\r
-       protected UserIdentity loadUser(String username) {\r
-               return null;\r
-       }\r
-}\r
diff --git a/src/com/gitblit/ServletRequestWrapper.java b/src/com/gitblit/ServletRequestWrapper.java
new file mode 100644 (file)
index 0000000..b97c395
--- /dev/null
@@ -0,0 +1,311 @@
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.IOException;\r
+import java.io.UnsupportedEncodingException;\r
+import java.security.Principal;\r
+import java.util.Enumeration;\r
+import java.util.Locale;\r
+import java.util.Map;\r
+\r
+import javax.servlet.RequestDispatcher;\r
+import javax.servlet.ServletInputStream;\r
+import javax.servlet.http.Cookie;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpSession;\r
+\r
+public abstract class ServletRequestWrapper implements HttpServletRequest {\r
+\r
+       protected final HttpServletRequest req;\r
+\r
+       public ServletRequestWrapper(HttpServletRequest req) {\r
+               this.req = req;\r
+       }\r
+\r
+       @Override\r
+       public Object getAttribute(String name) {\r
+               return req.getAttribute(name);\r
+       }\r
+\r
+       @Override\r
+       public Enumeration getAttributeNames() {\r
+               return req.getAttributeNames();\r
+       }\r
+\r
+       @Override\r
+       public String getCharacterEncoding() {\r
+               return req.getCharacterEncoding();\r
+       }\r
+\r
+       @Override\r
+       public void setCharacterEncoding(String env) throws UnsupportedEncodingException {\r
+               req.setCharacterEncoding(env);\r
+       }\r
+\r
+       @Override\r
+       public int getContentLength() {\r
+               return req.getContentLength();\r
+       }\r
+\r
+       @Override\r
+       public String getContentType() {\r
+               return req.getContentType();\r
+       }\r
+\r
+       @Override\r
+       public ServletInputStream getInputStream() throws IOException {\r
+               return req.getInputStream();\r
+       }\r
+\r
+       @Override\r
+       public String getParameter(String name) {\r
+               return req.getParameter(name);\r
+       }\r
+\r
+       @Override\r
+       public Enumeration getParameterNames() {\r
+               return req.getParameterNames();\r
+       }\r
+\r
+       @Override\r
+       public String[] getParameterValues(String name) {\r
+               return req.getParameterValues(name);\r
+       }\r
+\r
+       @Override\r
+       public Map getParameterMap() {\r
+               return req.getParameterMap();\r
+       }\r
+\r
+       @Override\r
+       public String getProtocol() {\r
+               return req.getProtocol();\r
+       }\r
+\r
+       @Override\r
+       public String getScheme() {\r
+               return req.getScheme();\r
+       }\r
+\r
+       @Override\r
+       public String getServerName() {\r
+               return req.getServerName();\r
+       }\r
+\r
+       @Override\r
+       public int getServerPort() {\r
+               return req.getServerPort();\r
+       }\r
+\r
+       @Override\r
+       public BufferedReader getReader() throws IOException {\r
+               return req.getReader();\r
+       }\r
+\r
+       @Override\r
+       public String getRemoteAddr() {\r
+               return req.getRemoteAddr();\r
+       }\r
+\r
+       @Override\r
+       public String getRemoteHost() {\r
+               return req.getRemoteHost();\r
+       }\r
+\r
+       @Override\r
+       public void setAttribute(String name, Object o) {\r
+               req.setAttribute(name, o);\r
+       }\r
+\r
+       @Override\r
+       public void removeAttribute(String name) {\r
+               req.removeAttribute(name);\r
+       }\r
+\r
+       @Override\r
+       public Locale getLocale() {\r
+               return req.getLocale();\r
+       }\r
+\r
+       @Override\r
+       public Enumeration getLocales() {\r
+               return req.getLocales();\r
+       }\r
+\r
+       @Override\r
+       public boolean isSecure() {\r
+               return req.isSecure();\r
+       }\r
+\r
+       @Override\r
+       public RequestDispatcher getRequestDispatcher(String path) {\r
+               return req.getRequestDispatcher(path);\r
+       }\r
+\r
+       @Override\r
+       @Deprecated\r
+       public String getRealPath(String path) {\r
+               return req.getRealPath(path);\r
+       }\r
+\r
+       @Override\r
+       public int getRemotePort() {\r
+               return req.getRemotePort();\r
+       }\r
+\r
+       @Override\r
+       public String getLocalName() {\r
+               return req.getLocalName();\r
+       }\r
+\r
+       @Override\r
+       public String getLocalAddr() {\r
+               return req.getLocalAddr();\r
+       }\r
+\r
+       @Override\r
+       public int getLocalPort() {\r
+               return req.getLocalPort();\r
+       }\r
+\r
+       @Override\r
+       public String getAuthType() {\r
+               return req.getAuthType();\r
+       }\r
+\r
+       @Override\r
+       public Cookie[] getCookies() {\r
+               return req.getCookies();\r
+       }\r
+\r
+       @Override\r
+       public long getDateHeader(String name) {\r
+               return req.getDateHeader(name);\r
+       }\r
+\r
+       @Override\r
+       public String getHeader(String name) {\r
+               return req.getHeader(name);\r
+       }\r
+\r
+       @Override\r
+       public Enumeration getHeaders(String name) {\r
+               return req.getHeaders(name);\r
+       }\r
+\r
+       @Override\r
+       public Enumeration getHeaderNames() {\r
+               return req.getHeaderNames();\r
+       }\r
+\r
+       @Override\r
+       public int getIntHeader(String name) {\r
+               return req.getIntHeader(name);\r
+       }\r
+\r
+       @Override\r
+       public String getMethod() {\r
+               return req.getMethod();\r
+       }\r
+\r
+       @Override\r
+       public String getPathInfo() {\r
+               return req.getPathInfo();\r
+       }\r
+\r
+       @Override\r
+       public String getPathTranslated() {\r
+               return req.getPathTranslated();\r
+       }\r
+\r
+       @Override\r
+       public String getContextPath() {\r
+               return req.getContextPath();\r
+       }\r
+\r
+       @Override\r
+       public String getQueryString() {\r
+               return req.getQueryString();\r
+       }\r
+\r
+       @Override\r
+       public String getRemoteUser() {\r
+               return req.getRemoteUser();\r
+       }\r
+\r
+       @Override\r
+       public boolean isUserInRole(String role) {\r
+               return req.isUserInRole(role);\r
+       }\r
+\r
+       @Override\r
+       public Principal getUserPrincipal() {\r
+               return req.getUserPrincipal();\r
+       }\r
+\r
+       @Override\r
+       public String getRequestedSessionId() {\r
+               return req.getRequestedSessionId();\r
+       }\r
+\r
+       @Override\r
+       public String getRequestURI() {\r
+               return req.getRequestURI();\r
+       }\r
+\r
+       @Override\r
+       public StringBuffer getRequestURL() {\r
+               return req.getRequestURL();\r
+       }\r
+\r
+       @Override\r
+       public String getServletPath() {\r
+               return req.getServletPath();\r
+       }\r
+\r
+       @Override\r
+       public HttpSession getSession(boolean create) {\r
+               return req.getSession(create);\r
+       }\r
+\r
+       @Override\r
+       public HttpSession getSession() {\r
+               return req.getSession();\r
+       }\r
+\r
+       @Override\r
+       public boolean isRequestedSessionIdValid() {\r
+               return req.isRequestedSessionIdValid();\r
+       }\r
+\r
+       @Override\r
+       public boolean isRequestedSessionIdFromCookie() {\r
+               return req.isRequestedSessionIdFromCookie();\r
+       }\r
+\r
+       @Override\r
+       public boolean isRequestedSessionIdFromURL() {\r
+               return req.isRequestedSessionIdFromURL();\r
+       }\r
+\r
+       @Override\r
+       @Deprecated\r
+       public boolean isRequestedSessionIdFromUrl() {\r
+               return req.isRequestedSessionIdFromUrl();\r
+       }\r
+}
\ No newline at end of file
diff --git a/src/com/gitblit/SyndicationFilter.java b/src/com/gitblit/SyndicationFilter.java
new file mode 100644 (file)
index 0000000..68f383b
--- /dev/null
@@ -0,0 +1,44 @@
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit;\r
+\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.models.RepositoryModel;\r
+import com.gitblit.models.UserModel;\r
+\r
+public class SyndicationFilter extends AccessRestrictionFilter {\r
+\r
+       @Override\r
+       protected String extractRepositoryName(String url) {\r
+               return url;\r
+       }\r
+\r
+       @Override\r
+       protected String getUrlRequestType(String url) {\r
+               return "RESTRICTED";\r
+       }\r
+\r
+       @Override\r
+       protected boolean requiresAuthentication(RepositoryModel repository) {\r
+               return repository.accessRestriction.atLeast(AccessRestrictionType.VIEW);\r
+       }\r
+\r
+       @Override\r
+       protected boolean canAccess(RepositoryModel repository, UserModel user, String restrictedURL) {\r
+               return user.canAccessRepository(repository.name);\r
+       }\r
+\r
+}\r
index d2b396eee696817da3997139969713d58f0217ee..19865fe541e84c2932b130ee5d1df2903c1e6261 100644 (file)
@@ -15,6 +15,7 @@
  */\r
 package com.gitblit;\r
 \r
+import java.text.MessageFormat;\r
 import java.util.List;\r
 \r
 import javax.servlet.http.HttpServlet;\r
@@ -28,6 +29,7 @@ import com.gitblit.models.RepositoryModel;
 import com.gitblit.utils.JGitUtils;\r
 import com.gitblit.utils.StringUtils;\r
 import com.gitblit.utils.SyndicationUtils;\r
+import com.gitblit.wicket.WicketUtils;\r
 \r
 public class SyndicationServlet extends HttpServlet {\r
 \r
@@ -36,20 +38,55 @@ public class SyndicationServlet extends HttpServlet {
        private transient Logger logger = LoggerFactory.getLogger(SyndicationServlet.class);\r
 \r
        public static String asLink(String baseURL, String repository, String objectId, int length) {\r
-               if (baseURL.charAt(baseURL.length() - 1) == '/') {\r
+               if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {\r
                        baseURL = baseURL.substring(0, baseURL.length() - 1);\r
                }\r
-               return baseURL + Constants.SYNDICATION_SERVLET_PATH + "?r=" + repository\r
-                               + (objectId == null ? "" : ("&h=" + objectId)) + (length > 0 ? "&l=" + length : "");\r
+               StringBuilder url = new StringBuilder();\r
+               url.append(baseURL);\r
+               url.append(Constants.SYNDICATION_SERVLET_PATH);\r
+               url.append(repository);\r
+               if (!StringUtils.isEmpty(objectId) || length > 0) {\r
+                       StringBuilder parameters = new StringBuilder("?");\r
+                       if (StringUtils.isEmpty(objectId)) {\r
+                               parameters.append("l=");\r
+                               parameters.append(length);\r
+                       } else {\r
+                               parameters.append("h=");\r
+                               parameters.append(objectId);\r
+                               if (length > 0) {\r
+                                       parameters.append("&l=");\r
+                                       parameters.append(length);\r
+                               }\r
+                       }\r
+                       url.append(parameters);\r
+               }\r
+               return url.toString();\r
+       }\r
+       \r
+       public static String getTitle(String repository, String objectId) {\r
+               String id = objectId;\r
+               if (!StringUtils.isEmpty(id)) {\r
+                       if (id.startsWith(org.eclipse.jgit.lib.Constants.R_HEADS)) {\r
+                               id = id.substring(org.eclipse.jgit.lib.Constants.R_HEADS.length());\r
+                       } else if (id.startsWith(org.eclipse.jgit.lib.Constants.R_REMOTES)) {\r
+                               id = id.substring(org.eclipse.jgit.lib.Constants.R_REMOTES.length());\r
+                       } else if (id.startsWith(org.eclipse.jgit.lib.Constants.R_TAGS)) {\r
+                               id = id.substring(org.eclipse.jgit.lib.Constants.R_TAGS.length());\r
+                       }\r
+               }\r
+               return MessageFormat.format("{0} ({1})", repository, id);\r
        }\r
 \r
        private void processRequest(javax.servlet.http.HttpServletRequest request,\r
                        javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,\r
                        java.io.IOException {\r
-               String hostUrl = request.getRequestURL().toString();\r
-               String servlet = request.getServletPath();\r
-               hostUrl = hostUrl.substring(0, hostUrl.indexOf(servlet));\r
-               String repositoryName = request.getParameter("r");\r
+\r
+               String hostURL = WicketUtils.getHostURL(request);\r
+               String url = request.getRequestURI().substring(request.getServletPath().length());\r
+               if (url.charAt(0) == '/' && url.length() > 1) {\r
+                       url = url.substring(1);\r
+               }\r
+               String repositoryName = url;\r
                String objectId = request.getParameter("h");\r
                String l = request.getParameter("l");\r
                int length = GitBlit.getInteger(Keys.web.syndicationEntries, 25);\r
@@ -62,14 +99,13 @@ public class SyndicationServlet extends HttpServlet {
                        } catch (NumberFormatException x) {\r
                        }\r
                }\r
-               \r
-               // TODO confirm repository is accessible!!\r
 \r
                Repository repository = GitBlit.self().getRepository(repositoryName);\r
                RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName);\r
                List<RevCommit> commits = JGitUtils.getRevLog(repository, objectId, 0, length);\r
                try {\r
-                       SyndicationUtils.toRSS(hostUrl, model.name + " " + objectId, model.description, model.name, commits, response.getOutputStream());\r
+                       SyndicationUtils.toRSS(hostURL, getTitle(model.name, objectId), model.description,\r
+                                       model.name, commits, response.getOutputStream());\r
                } catch (Exception e) {\r
                        logger.error("An error occurred during feed generation", e);\r
                }\r
index f23fd291d2453ee323170d7faad1b32848ff63c9..29647088df234c00e3f89c7e7a491dc83f9c8f10 100644 (file)
 package com.gitblit.models;\r
 \r
 import java.io.Serializable;\r
+import java.security.Principal;\r
 import java.util.ArrayList;\r
 import java.util.List;\r
 \r
-public class UserModel implements Serializable {\r
+public class UserModel implements Principal, Serializable {\r
 \r
        private static final long serialVersionUID = 1L;\r
 \r
@@ -41,6 +42,11 @@ public class UserModel implements Serializable {
                repositories.add(name.toLowerCase());\r
        }\r
 \r
+       @Override\r
+       public String getName() {       \r
+               return username;\r
+       }\r
+\r
        @Override\r
        public String toString() {\r
                return username;\r
index fa84fe8fc543a3aaf41c8dfa4dd5996c50fd86ec..363efc9c1dfc86acac29df7e961b6f13c3ed829c 100644 (file)
 package com.gitblit.utils;\r
 \r
 import java.io.UnsupportedEncodingException;\r
+import java.nio.charset.Charset;\r
 import java.security.MessageDigest;\r
 import java.security.NoSuchAlgorithmException;\r
 import java.util.ArrayList;\r
 import java.util.List;\r
 import java.util.regex.PatternSyntaxException;\r
 \r
+import org.eclipse.jetty.util.log.Log;\r
+import org.eclipse.jgit.util.Base64;\r
+\r
 public class StringUtils {\r
 \r
+       public static final String MD5_TYPE = "MD5:";\r
+\r
        public static boolean isEmpty(String value) {\r
                return value == null || value.trim().length() == 0;\r
        }\r
@@ -56,6 +62,22 @@ public class StringUtils {
                return retStr.toString();\r
        }\r
 \r
+       public static String encodeURL(String inStr) {\r
+               StringBuffer retStr = new StringBuffer();\r
+               int i = 0;\r
+               while (i < inStr.length()) {\r
+                       if (inStr.charAt(i) == '/') {\r
+                               retStr.append("%2F");\r
+                       } else if (inStr.charAt(i) == ' ') {\r
+                               retStr.append("%20");\r
+                       } else {\r
+                               retStr.append(inStr.charAt(i));\r
+                       }\r
+                       i++;\r
+               }\r
+               return retStr.toString();\r
+       }\r
+\r
        public static String flattenStrings(List<String> values) {\r
                return flattenStrings(values, " ");\r
        }\r
@@ -116,20 +138,41 @@ public class StringUtils {
                try {\r
                        MessageDigest md = MessageDigest.getInstance("SHA-1");\r
                        md.update(bytes, 0, bytes.length);\r
-                       byte[] sha1hash = md.digest();\r
-                       StringBuilder sb = new StringBuilder(sha1hash.length * 2);\r
-                       for (int i = 0; i < sha1hash.length; i++) {\r
-                               if (((int) sha1hash[i] & 0xff) < 0x10) {\r
-                                       sb.append('0');\r
-                               }\r
-                               sb.append(Long.toString((int) sha1hash[i] & 0xff, 16));\r
-                       }\r
-                       return sb.toString();\r
+                       byte[] digest = md.digest();\r
+                       return toHex(digest);\r
                } catch (NoSuchAlgorithmException t) {\r
                        throw new RuntimeException(t);\r
                }\r
        }\r
 \r
+       public static String getMD5(String string) {\r
+               try {\r
+                       MessageDigest md = MessageDigest.getInstance("MD5");\r
+                       md.reset();\r
+                       md.update(string.getBytes("iso-8859-1"));\r
+                       byte[] digest = md.digest();\r
+                       return toHex(digest);\r
+               } catch (Exception e) {\r
+                       Log.warn(e);\r
+                       return null;\r
+               }\r
+       }\r
+\r
+       private static String toHex(byte[] bytes) {\r
+               StringBuilder sb = new StringBuilder(bytes.length * 2);\r
+               for (int i = 0; i < bytes.length; i++) {\r
+                       if (((int) bytes[i] & 0xff) < 0x10) {\r
+                               sb.append('0');\r
+                       }\r
+                       sb.append(Long.toString((int) bytes[i] & 0xff, 16));\r
+               }\r
+               return sb.toString();\r
+       }\r
+\r
+       public static String decodeBase64(String base64) {\r
+               return new String(Base64.decode(base64), Charset.forName("UTF-8"));\r
+       }\r
+\r
        public static String getRootPath(String path) {\r
                if (path.indexOf('/') > -1) {\r
                        return path.substring(0, path.lastIndexOf('/'));\r
@@ -144,11 +187,11 @@ public class StringUtils {
                }\r
                return relativePath;\r
        }\r
-       \r
+\r
        public static List<String> getStringsFromValue(String value) {\r
                return getStringsFromValue(value, " ");\r
        }\r
-               \r
+\r
        public static List<String> getStringsFromValue(String value, String separator) {\r
                List<String> strings = new ArrayList<String>();\r
                try {\r
index da937f9aed975fadb4179f580af7064eebfdc9dc..5763af3f343902ad34daa2b13d1a30d37b8ea42b 100644 (file)
@@ -24,32 +24,41 @@ import java.util.List;
 \r
 import org.eclipse.jgit.revwalk.RevCommit;\r
 \r
+import com.gitblit.Constants;\r
 import com.sun.syndication.feed.synd.SyndContent;\r
 import com.sun.syndication.feed.synd.SyndContentImpl;\r
 import com.sun.syndication.feed.synd.SyndEntry;\r
 import com.sun.syndication.feed.synd.SyndEntryImpl;\r
 import com.sun.syndication.feed.synd.SyndFeed;\r
 import com.sun.syndication.feed.synd.SyndFeedImpl;\r
+import com.sun.syndication.feed.synd.SyndImageImpl;\r
 import com.sun.syndication.io.FeedException;\r
 import com.sun.syndication.io.SyndFeedOutput;\r
 \r
 public class SyndicationUtils {\r
 \r
-       public static void toRSS(String hostUrl, String title, String description, String repository, List<RevCommit> commits, OutputStream os)\r
-                       throws IOException, FeedException {\r
+       public static void toRSS(String hostUrl, String title, String description, String repository,\r
+                       List<RevCommit> commits, OutputStream os) throws IOException, FeedException {\r
 \r
                SyndFeed feed = new SyndFeedImpl();\r
-               feed.setFeedType("rss_1.0");\r
+               feed.setFeedType("rss_2.0");\r
                feed.setTitle(title);\r
-               feed.setLink(MessageFormat.format("{0}/summary/{1}", hostUrl, repository));\r
+               feed.setLink(MessageFormat.format("{0}/summary/{1}", hostUrl,\r
+                               StringUtils.encodeURL(repository)));\r
                feed.setDescription(description);\r
+               SyndImageImpl image = new SyndImageImpl();\r
+               image.setTitle(Constants.NAME);\r
+               image.setUrl(hostUrl + Constants.RESOURCE_PATH + "gitblt_25.png");\r
+               image.setLink(hostUrl);\r
+               feed.setImage(image);\r
 \r
                List<SyndEntry> entries = new ArrayList<SyndEntry>();\r
                for (RevCommit commit : commits) {\r
                        SyndEntry entry = new SyndEntryImpl();\r
                        entry.setTitle(commit.getShortMessage());\r
                        entry.setAuthor(commit.getAuthorIdent().getName());\r
-                       entry.setLink(MessageFormat.format("{0}/commit/{1}/{2}", hostUrl, repository, commit.getName()));\r
+                       entry.setLink(MessageFormat.format("{0}/commit/{1}/{2}", hostUrl,\r
+                                       StringUtils.encodeURL(repository), commit.getName()));\r
                        entry.setPublishedDate(commit.getCommitterIdent().getWhen());\r
 \r
                        SyndContent content = new SyndContentImpl();\r
index 472a11dc5c194d987fac4612a24dc7108076ae05..cc54e003a57fbe68423c288b9fa5fffbbd51ffdd 100644 (file)
@@ -122,7 +122,7 @@ public class GitBlitWebApp extends WebApplication {
 \r
        @Override\r
        public final String getConfigurationType() {\r
-               if (GitBlit.self().isDebugMode()) {\r
+               if (GitBlit.isDebugMode()) {\r
                        return Application.DEVELOPMENT;\r
                }\r
                return Application.DEPLOYMENT;\r
index 1d2a60fe879455cc485e1c4853989ea2dc5470fc..54f9648cd91839349c4aebac47a2f9a1edbb5817 100644 (file)
@@ -22,11 +22,18 @@ import java.util.Date;
 import java.util.List;\r
 import java.util.TimeZone;\r
 \r
+import javax.servlet.http.HttpServletRequest;\r
+\r
 import org.apache.wicket.Component;\r
 import org.apache.wicket.PageParameters;\r
+import org.apache.wicket.Request;\r
+import org.apache.wicket.behavior.HeaderContributor;\r
 import org.apache.wicket.behavior.SimpleAttributeModifier;\r
+import org.apache.wicket.markup.html.IHeaderContributor;\r
+import org.apache.wicket.markup.html.IHeaderResponse;\r
 import org.apache.wicket.markup.html.basic.Label;\r
 import org.apache.wicket.markup.html.image.ContextImage;\r
+import org.apache.wicket.protocol.http.WebRequest;\r
 import org.apache.wicket.resource.ContextRelativeResource;\r
 import org.eclipse.jgit.diff.DiffEntry.ChangeType;\r
 import org.eclipse.jgit.lib.Constants;\r
@@ -162,7 +169,7 @@ public class WicketUtils {
        }\r
 \r
        public static ContextImage newImage(String wicketId, String file, String tooltip) {\r
-               ContextImage img = new ContextImage(wicketId, "/com/gitblit/wicket/resources/" + file);\r
+               ContextImage img = new ContextImage(wicketId, com.gitblit.Constants.RESOURCE_PATH + file);\r
                if (!StringUtils.isEmpty(tooltip)) {\r
                        setHtmlTooltip(img, tooltip);\r
                }\r
@@ -170,7 +177,42 @@ public class WicketUtils {
        }\r
 \r
        public static ContextRelativeResource getResource(String file) {\r
-               return new ContextRelativeResource("/com/gitblit/wicket/resources/" + file);\r
+               return new ContextRelativeResource(com.gitblit.Constants.RESOURCE_PATH + file);\r
+       }\r
+\r
+       public static String getHostURL(Request request) {\r
+               HttpServletRequest req = ((WebRequest) request).getHttpServletRequest();\r
+               return getHostURL(req);\r
+       }\r
+\r
+       public static String getHostURL(HttpServletRequest request) {\r
+               StringBuilder sb = new StringBuilder();\r
+               sb.append(request.getScheme());\r
+               sb.append("://");\r
+               sb.append(request.getServerName());\r
+               if ((request.getScheme().equals("http") && request.getServerPort() != 80)\r
+                               || (request.getScheme().equals("https") && request.getServerPort() != 443)) {\r
+                       sb.append(":" + request.getServerPort());\r
+               }\r
+               return sb.toString();\r
+       }\r
+\r
+       public static HeaderContributor syndicationDiscoveryLink(final String feedTitle,\r
+                       final String url) {\r
+               return new HeaderContributor(new IHeaderContributor() {\r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       public void renderHead(IHeaderResponse response) {\r
+                               String contentType = "application/rss+xml";\r
+\r
+                               StringBuffer buffer = new StringBuffer();\r
+                               buffer.append("<link rel=\"alternate\" ");\r
+                               buffer.append("type=\"").append(contentType).append("\" ");\r
+                               buffer.append("title=\"").append(feedTitle).append("\" ");\r
+                               buffer.append("href=\"").append(url).append("\" />");\r
+                               response.renderString(buffer.toString());\r
+                       }\r
+               });\r
        }\r
 \r
        public static PageParameters newUsernameParameter(String username) {\r
index 6da962ef0b336d922ee5223e54407500ce5b1bd4..a34917b62a0e06a680307794eabc3a899ff0a1ea 100644 (file)
@@ -128,7 +128,7 @@ public class CommitPage extends RepositoryPage {
                                                SearchType.AUTHOR));\r
                                item.add(WicketUtils.createTimestampLabel("authorDate", entry.notesRef\r
                                                .getAuthorIdent().getWhen(), getTimeZone()));\r
-                               item.add(new Label("noteContent", substituteText(entry.content))\r
+                               item.add(new Label("noteContent", GitBlit.self().processCommitMessage(repositoryName, entry.content))\r
                                                .setEscapeModelStrings(false));\r
                        }\r
                };\r
index eafec05d5859b757877718ea40fbae3462a679ff..63916276725bd32d6d44a90816270a03feee6962 100644 (file)
@@ -31,8 +31,6 @@ import org.apache.wicket.model.CompoundPropertyModel;
 import org.apache.wicket.model.Model;\r
 import org.apache.wicket.model.util.CollectionModel;\r
 import org.apache.wicket.model.util.ListModel;\r
-import org.eclipse.jetty.http.security.Credential.Crypt;\r
-import org.eclipse.jetty.http.security.Credential.MD5;\r
 \r
 import com.gitblit.Constants.AccessRestrictionType;\r
 import com.gitblit.GitBlit;\r
@@ -114,8 +112,7 @@ public class EditUserPage extends BasePage {
                                        return;\r
                                }\r
                                String password = userModel.password;\r
-                               if (!password.toUpperCase().startsWith(Crypt.__TYPE)\r
-                                               && !password.toUpperCase().startsWith(MD5.__TYPE)) {\r
+                               if (!password.toUpperCase().startsWith(StringUtils.MD5_TYPE)) {\r
                                        // This is a plain text password.\r
                                        // Check length.\r
                                        int minLength = GitBlit.getInteger(Keys.realm.minPasswordLength, 5);\r
@@ -133,7 +130,7 @@ public class EditUserPage extends BasePage {
                                        String type = GitBlit.getString(Keys.realm.passwordStorage, "md5");\r
                                        if (type.equalsIgnoreCase("md5")) {\r
                                                // store MD5 digest of password\r
-                                               userModel.password = MD5.digest(userModel.password);\r
+                                               userModel.password = StringUtils.MD5_TYPE + StringUtils.getMD5(userModel.password);\r
                                        }\r
                                }\r
 \r
index 35f8a73fa448f0c5f440278aa518ba904bcf1d5a..2cd787c7af9358042b5bee90cf056c4454dd0f22 100644 (file)
@@ -26,6 +26,8 @@ public class LogPage extends RepositoryPage {
        public LogPage(PageParameters params) {\r
                super(params);\r
 \r
+               addSyndicationDiscoveryLink();\r
+               \r
                int pageNumber = WicketUtils.getPage(params);\r
                int prevPage = Math.max(0, pageNumber - 1);\r
                int nextPage = pageNumber + 1;\r
index c3a6b03b1f5864d09ba23a4a3f9cb7cb1c0ca1d1..cf14ee192fe655cc3b76b53446767920d84b7441 100644 (file)
@@ -21,7 +21,6 @@ import java.util.Arrays;
 import java.util.HashMap;\r
 import java.util.List;\r
 import java.util.Map;\r
-import java.util.Map.Entry;\r
 \r
 import org.apache.wicket.Component;\r
 import org.apache.wicket.PageParameters;\r
@@ -159,7 +158,7 @@ public abstract class RepositoryPage extends BasePage {
                        }\r
                };\r
                add(extrasView);\r
-               \r
+\r
                add(new ExternalLink("syndication", SyndicationServlet.asLink(getRequest()\r
                                .getRelativePathPrefixToContextRoot(), repositoryName, null, 0)));\r
 \r
@@ -189,6 +188,12 @@ public abstract class RepositoryPage extends BasePage {
                }\r
        }\r
 \r
+       protected void addSyndicationDiscoveryLink() {\r
+               add(WicketUtils.syndicationDiscoveryLink(SyndicationServlet.getTitle(repositoryName,\r
+                               objectId), SyndicationServlet.asLink(getRequest()\r
+                               .getRelativePathPrefixToContextRoot(), repositoryName, objectId, 0)));\r
+       }\r
+\r
        protected Repository getRepository() {\r
                if (r == null) {\r
                        Repository r = GitBlit.self().getRepository(repositoryName);\r
@@ -234,48 +239,13 @@ public abstract class RepositoryPage extends BasePage {
        protected void addFullText(String wicketId, String text, boolean substituteRegex) {\r
                String html;\r
                if (substituteRegex) {\r
-                       html = substituteText(text);\r
+                       html = GitBlit.self().processCommitMessage(repositoryName, text);\r
                } else {\r
                        html = StringUtils.breakLinesForHtml(text);\r
                }\r
                add(new Label(wicketId, html).setEscapeModelStrings(false));\r
        }\r
 \r
-       protected String substituteText(String text) {\r
-               String html = StringUtils.breakLinesForHtml(text);\r
-               Map<String, String> map = new HashMap<String, String>();\r
-               // global regex keys\r
-               if (GitBlit.getBoolean(Keys.regex.global, false)) {\r
-                       for (String key : GitBlit.getAllKeys(Keys.regex.global)) {\r
-                               if (!key.equals(Keys.regex.global)) {\r
-                                       String subKey = key.substring(key.lastIndexOf('.') + 1);\r
-                                       map.put(subKey, GitBlit.getString(key, ""));\r
-                               }\r
-                       }\r
-               }\r
-\r
-               // repository-specific regex keys\r
-               List<String> keys = GitBlit.getAllKeys(Keys.regex._ROOT + "."\r
-                               + repositoryName.toLowerCase());\r
-               for (String key : keys) {\r
-                       String subKey = key.substring(key.lastIndexOf('.') + 1);\r
-                       map.put(subKey, GitBlit.getString(key, ""));\r
-               }\r
-\r
-               for (Entry<String, String> entry : map.entrySet()) {\r
-                       String definition = entry.getValue().trim();\r
-                       String[] chunks = definition.split("!!!");\r
-                       if (chunks.length == 2) {\r
-                               html = html.replaceAll(chunks[0], chunks[1]);\r
-                       } else {\r
-                               logger.warn(entry.getKey()\r
-                                               + " improperly formatted.  Use !!! to separate match from replacement: "\r
-                                               + definition);\r
-                       }\r
-               }\r
-               return html;\r
-       }\r
-\r
        protected abstract String getPageName();\r
 \r
        protected Component createPersonPanel(String wicketId, PersonIdent identity,\r
index e85901ab6aa1ceb8cfa61f8e0e676eee9463fe46..0d0db86bd7715cc5c669535ea31becb4339628cf 100644 (file)
@@ -22,12 +22,9 @@ import java.text.ParseException;
 import java.util.ArrayList;\r
 import java.util.List;\r
 \r
-import javax.servlet.http.HttpServletRequest;\r
-\r
 import org.apache.wicket.PageParameters;\r
 import org.apache.wicket.markup.html.basic.Label;\r
 import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
-import org.apache.wicket.protocol.http.WebRequest;\r
 import org.eclipse.jgit.lib.Repository;\r
 import org.eclipse.jgit.revwalk.RevCommit;\r
 import org.wicketstuff.googlecharts.Chart;\r
@@ -81,6 +78,8 @@ public class SummaryPage extends RepositoryPage {
                        metrics = MetricUtils.getDateMetrics(r, null, true, null);\r
                        metricsTotal = metrics.remove(0);\r
                }\r
+               \r
+               addSyndicationDiscoveryLink();\r
 \r
                // repository description\r
                add(new Label("repositoryDescription", getRepositoryModel().description));\r
@@ -121,17 +120,8 @@ public class SummaryPage extends RepositoryPage {
                        default:\r
                                add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false));\r
                        }\r
-\r
-                       HttpServletRequest req = ((WebRequest) getRequestCycle().getRequest())\r
-                                       .getHttpServletRequest();\r
                        StringBuilder sb = new StringBuilder();\r
-                       sb.append(req.getScheme());\r
-                       sb.append("://");\r
-                       sb.append(req.getServerName());\r
-                       if ((req.getScheme().equals("http") && req.getServerPort() != 80)\r
-                                       || (req.getScheme().equals("https") && req.getServerPort() != 443)) {\r
-                               sb.append(":" + req.getServerPort());\r
-                       }\r
+                       sb.append(WicketUtils.getHostURL(getRequestCycle().getRequest()));                                      \r
                        sb.append(Constants.GIT_SERVLET_PATH);\r
                        sb.append(repositoryName);\r
                        repositoryUrls.add(sb.toString());\r
index a599d22671bf748d7d343fce7e7e3f882621f3f6..1e609e10708bdc1e27815ce8c6ab71c5be87de8e 100644 (file)
@@ -50,7 +50,7 @@
                        <th wicket:id="orderByOwner"><wicket:message key="gb.owner">Owner</wicket:message></th>\r
                        <th></th>\r
                        <th wicket:id="orderByDate"><wicket:message key="gb.lastChange">Last Change</wicket:message></th>\r
-                       <th clas="right"></th>\r
+                       <th class="right"></th>\r
                </tr>\r
        </wicket:fragment>\r
        \r
         <td class="author"><span wicket:id="repositoryOwner">[repository owner]</span></td>\r
         <td style="text-align: right;padding-right:10px;"><img class="inlineIcon" wicket:id="ticketsIcon" /><img class="inlineIcon" wicket:id="docsIcon" /><img class="inlineIcon" wicket:id="frozenIcon" /><img class="inlineIcon" wicket:id="accessRestrictionIcon" /></td>\r
         <td><span wicket:id="repositoryLastChange">[last change]</span></td>\r
-        <td class="rightAlign"><span wicket:id="repositoryLinks"></span></td>\r
+        <td class="rightAlign">\r
+               <span wicket:id="repositoryLinks"></span>\r
+                       <a style="text-decoration: none;" wicket:id="syndication">\r
+                               <img style="border:0px;vertical-align:middle;" src="/com/gitblit/wicket/resources/feed_16x16.png"></img>\r
+                       </a>\r
+               </td>                           \r
        </wicket:fragment>\r
        \r
 </wicket:panel>\r
index a0c9e13222524b1c141096170618ef37c6a8a5b9..c7441487fabc772839266b4034deff6d56fde179 100644 (file)
@@ -31,6 +31,7 @@ import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;\r
 import org.apache.wicket.markup.html.basic.Label;\r
 import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
+import org.apache.wicket.markup.html.link.ExternalLink;\r
 import org.apache.wicket.markup.html.link.Link;\r
 import org.apache.wicket.markup.html.panel.Fragment;\r
 import org.apache.wicket.markup.repeater.Item;\r
@@ -43,6 +44,7 @@ import org.apache.wicket.model.Model;
 import com.gitblit.Constants.AccessRestrictionType;\r
 import com.gitblit.GitBlit;\r
 import com.gitblit.Keys;\r
+import com.gitblit.SyndicationServlet;\r
 import com.gitblit.models.RepositoryModel;\r
 import com.gitblit.models.UserModel;\r
 import com.gitblit.utils.StringUtils;\r
@@ -215,6 +217,8 @@ public class RepositoriesPanel extends BasePanel {
                                } else {\r
                                        row.add(new Label("repositoryLinks"));\r
                                }\r
+                               row.add(new ExternalLink("syndication", SyndicationServlet.asLink(getRequest()\r
+                                               .getRelativePathPrefixToContextRoot(), entry.name, null, 0)));\r
                                WicketUtils.setAlternatingBackground(item, counter);\r
                                counter++;\r
                        }\r
index e13e1bbfd4d84b2bc5f90fc5d4f5cadbe18ee7a0..c9e383e793853473c6204189aa70049c0c8e8fac 100644 (file)
@@ -24,10 +24,10 @@ import junit.framework.TestSuite;
 import org.eclipse.jgit.lib.Repository;\r
 import org.eclipse.jgit.storage.file.FileRepository;\r
 \r
+import com.gitblit.FileLoginService;\r
 import com.gitblit.FileSettings;\r
 import com.gitblit.GitBlit;\r
 import com.gitblit.GitBlitException;\r
-import com.gitblit.JettyLoginService;\r
 import com.gitblit.models.RepositoryModel;\r
 import com.gitblit.utils.JGitUtils;\r
 \r
@@ -72,8 +72,7 @@ public class GitBlitSuite extends TestSetup {
        protected void setUp() throws Exception {\r
                FileSettings settings = new FileSettings("distrib/gitblit.properties");\r
                GitBlit.self().configureContext(settings);\r
-               JettyLoginService loginService = new JettyLoginService(new File("distrib/users.properties"));\r
-               loginService.loadUsers();\r
+               FileLoginService loginService = new FileLoginService(new File("distrib/users.properties"));\r
                GitBlit.self().setLoginService(loginService);\r
 \r
                if (REPOSITORIES.exists() || REPOSITORIES.mkdirs()) {\r