]> source.dussan.org Git - gitblit.git/commitdiff
Consolidate authentication techniques and support container principals (issue-68)
authorJames Moger <james.moger@gitblit.com>
Mon, 3 Dec 2012 21:59:17 +0000 (16:59 -0500)
committerJames Moger <james.moger@gitblit.com>
Mon, 3 Dec 2012 21:59:17 +0000 (16:59 -0500)
docs/04_releases.mkd
src/com/gitblit/AuthenticationFilter.java
src/com/gitblit/Constants.java
src/com/gitblit/GitBlit.java

index 3f03160a1e0141896106895d38dc217d729ac3fa..bf57d118fad656c883d1eb59020aab10fcf2db88 100644 (file)
@@ -72,7 +72,7 @@ This is extreme and should be considered carefully since it affects every https
 \r
 #### changes\r
 \r
-- Access restricted servlets (e.g. DownloadZip, RSS, etc) will try to authenticate any Gitblit cookie found in the request before resorting to BASIC authentication.\r
+- All access restricted servlets (e.g. DownloadZip, RSS, etc) will try to authenticate using X509 certificates, container principals, cookies, and BASIC headers, in that order.\r
 - Added *groovy* and *scala* to *web.prettyPrintExtensions*\r
 - Added short commit id column to log and history tables (issue 168)\r
 - Teams can now specify the *admin*, *create*, and *fork* roles to simplify user administration\r
@@ -83,15 +83,17 @@ This is extreme and should be considered carefully since it affects every https
 - Emit a warning in the log file if running on a Tomcat-based servlet container which is unfriendly to %2F forward-slash url encoding AND Gitblit is configured to mount parameters with %2F forward-slash url encoding (Github/jpyeron, issue 126)\r
 - LDAP admin attribute setting is now consistent with LDAP teams setting and admin teams list.  \r
 If *realm.ldap.maintainTeams==true* **AND** *realm.ldap.admins* is not empty, then User.canAdmin() is controlled by LDAP administrative team membership.  Otherwise, User.canAdmin() is controlled by Gitblit.\r
+- Support servlet container authentication for existing UserModels (issue 68)\r
 \r
 #### dependency changes\r
 \r
-- updated to Jetty 7.6.7\r
+- updated to Jetty 7.6.8\r
 - updated to JGit 2.1.0.201209190230-r\r
 - updated to Groovy 1.8.8\r
 - updated to Wicket 1.4.21\r
 - updated to Lucene 3.6.1\r
 - updated to BouncyCastle 1.47\r
+- updated to MarkdownPapers 1.3.2\r
 - added JCalendar 1.3.2\r
 - added Commons-Compress 1.4.1\r
 - added XZ for Java 1.0\r
index 64aa44111e452380cf5051308a8be22fe8f45c8d..eb6e95b72d7c49d14f47fa27d236fce308ce0bc2 100644 (file)
@@ -16,9 +16,7 @@
 package com.gitblit;\r
 \r
 import java.io.IOException;\r
-import java.nio.charset.Charset;\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
@@ -37,7 +35,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;\r
 \r
 import com.gitblit.models.UserModel;\r
-import com.gitblit.utils.Base64;\r
 import com.gitblit.utils.StringUtils;\r
 \r
 /**\r
@@ -51,9 +48,7 @@ import com.gitblit.utils.StringUtils;
  */\r
 public abstract class AuthenticationFilter implements Filter {\r
 \r
-       protected static final String BASIC = "Basic";\r
-\r
-       protected static final String CHALLENGE = BASIC + " realm=\"" + Constants.NAME + "\"";\r
+       protected static final String CHALLENGE = "Basic realm=\"" + Constants.NAME + "\"";\r
 \r
        protected static final String SESSION_SECURED = "com.gitblit.secured";\r
 \r
@@ -103,40 +98,8 @@ public abstract class AuthenticationFilter implements Filter {
         * @return user\r
         */\r
        protected UserModel getUser(HttpServletRequest httpRequest) {\r
-               UserModel user = null;\r
-               // try request authentication\r
-               user = GitBlit.self().authenticate(httpRequest);\r
-               if (user != null) {\r
-                       return user;\r
-               } else if (requiresClientCertificate()) {\r
-                       // http request does not have a valid certificate\r
-                       // and the filter requires one\r
-                       return null;\r
-               }\r
-               \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 = new String(Base64.decode(base64Credentials),\r
-                                       Charset.forName("UTF-8"));\r
-                       // credentials = username:password\r
-                       final String[] values = credentials.split(":",2);\r
-\r
-                       if (values.length == 2) {\r
-                               String username = values[0];\r
-                               char[] password = values[1].toCharArray();\r
-                               user = GitBlit.self().authenticate(username, password);\r
-                               if (user != null) {\r
-                                       return user;\r
-                               }\r
-                       }\r
-                       if (GitBlit.isDebugMode()) {\r
-                               logger.info(MessageFormat.format("AUTH: invalid credentials ({0})", credentials));\r
-                       }\r
-               }\r
-               return null;\r
+               UserModel user = GitBlit.self().authenticate(httpRequest, requiresClientCertificate());\r
+               return user;\r
        }\r
 \r
        /**\r
index 4669c4c9111296f551a0f69ea8454ab59bebb9ff..d152651bf349ffe8005f21865005fca931366a08 100644 (file)
@@ -399,7 +399,7 @@ public class Constants {
        }\r
 \r
        public static enum AuthenticationType {\r
-               CREDENTIALS, COOKIE, CERTIFICATE;\r
+               CREDENTIALS, COOKIE, CERTIFICATE, CONTAINER;\r
                \r
                public boolean isStandard() {\r
                        return ordinal() <= COOKIE.ordinal();\r
index 69135c495db31dec1f40c5fbe1e810f11532444a..02906ee99986dd7949edd4ce8776b3466fdc84ff 100644 (file)
@@ -24,6 +24,8 @@ import java.io.InputStreamReader;
 import java.lang.reflect.Field;\r
 import java.net.URI;\r
 import java.net.URISyntaxException;\r
+import java.nio.charset.Charset;\r
+import java.security.Principal;\r
 import java.text.MessageFormat;\r
 import java.text.SimpleDateFormat;\r
 import java.util.ArrayList;\r
@@ -98,6 +100,7 @@ import com.gitblit.models.SettingModel;
 import com.gitblit.models.TeamModel;\r
 import com.gitblit.models.UserModel;\r
 import com.gitblit.utils.ArrayUtils;\r
+import com.gitblit.utils.Base64;\r
 import com.gitblit.utils.ByteFormat;\r
 import com.gitblit.utils.ContainerUtils;\r
 import com.gitblit.utils.DeepCopier;\r
@@ -567,6 +570,20 @@ public class GitBlit implements ServletContextListener {
         * @return a user object or null\r
         */\r
        public UserModel authenticate(HttpServletRequest httpRequest) {\r
+               return authenticate(httpRequest, false);\r
+       }\r
+       \r
+       /**\r
+        * Authenticate a user based on HTTP request parameters.\r
+        * \r
+        * Authentication by X509Certificate, servlet container principal, cookie,\r
+        * and BASIC header.\r
+        * \r
+        * @param httpRequest\r
+        * @param requiresCertificate\r
+        * @return a user object or null\r
+        */\r
+       public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) {\r
                // try to authenticate by certificate\r
                boolean checkValidity = settings.getBoolean(Keys.git.enforceCertificateValidity, true);\r
                String [] oids = getStrings(Keys.git.certificateUsernameOIDs).toArray(new String[0]);\r
@@ -574,39 +591,85 @@ public class GitBlit implements ServletContextListener {
                if (model != null) {\r
                        // grab real user model and preserve certificate serial number\r
                        UserModel user = getUserModel(model.username);\r
+                       X509Metadata metadata = HttpUtils.getCertificateMetadata(httpRequest);\r
                        if (user != null) {\r
-                               RequestCycle requestCycle = RequestCycle.get();\r
-                               if (requestCycle != null) {\r
-                                       // flag the Wicket session, if this is a Wicket request\r
-                                       GitBlitWebSession session = GitBlitWebSession.get();\r
-                                       session.authenticationType = AuthenticationType.CERTIFICATE;\r
-                               }\r
-                               X509Metadata metadata = HttpUtils.getCertificateMetadata(httpRequest);\r
+                               flagWicketSession(AuthenticationType.CERTIFICATE);\r
                                logger.info(MessageFormat.format("{0} authenticated by client certificate {1} from {2}",\r
                                                user.username, metadata.serialNumber, httpRequest.getRemoteAddr()));\r
                                return user;\r
+                       } else {\r
+                               logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted client certificate ({1}) authentication from {2}",\r
+                                               model.username, metadata.serialNumber, httpRequest.getRemoteAddr()));\r
+                       }\r
+               }\r
+               \r
+               if (requiresCertificate) {\r
+                       // caller requires client certificate authentication (e.g. git servlet)\r
+                       return null;\r
+               }\r
+               \r
+               // try to authenticate by servlet container principal\r
+               Principal principal = httpRequest.getUserPrincipal();\r
+               if (principal != null) {\r
+                       UserModel user = getUserModel(principal.getName());\r
+                       if (user != null) {\r
+                               flagWicketSession(AuthenticationType.CONTAINER);\r
+                               logger.info(MessageFormat.format("{0} authenticated by servlet container principal from {1}",\r
+                                               user.username, httpRequest.getRemoteAddr()));\r
+                               return user;\r
+                       } else {\r
+                               logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted servlet container authentication from {1}",\r
+                                               principal.getName(), httpRequest.getRemoteAddr()));\r
                        }\r
                }\r
                \r
                // try to authenticate by cookie\r
-               Cookie[] cookies = httpRequest.getCookies();\r
-               if (allowCookieAuthentication() && cookies != null && cookies.length > 0) {\r
-                       // Grab cookie from Browser Session\r
-                       UserModel user = authenticate(cookies);\r
+               if (allowCookieAuthentication()) {\r
+                       UserModel user = authenticate(httpRequest.getCookies());\r
                        if (user != null) {\r
-                               RequestCycle requestCycle = RequestCycle.get();\r
-                               if (requestCycle != null) {\r
-                                       // flag the Wicket session, if this is a Wicket request\r
-                                       GitBlitWebSession session = GitBlitWebSession.get();\r
-                                       session.authenticationType = AuthenticationType.COOKIE;\r
-                               }\r
+                               flagWicketSession(AuthenticationType.COOKIE);\r
                                logger.info(MessageFormat.format("{0} authenticated by cookie from {1}",\r
                                                user.username, httpRequest.getRemoteAddr()));\r
                                return user;\r
                        }\r
                }\r
+               \r
+               // try to authenticate by BASIC\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 = new String(Base64.decode(base64Credentials),\r
+                                       Charset.forName("UTF-8"));\r
+                       // credentials = username:password\r
+                       final String[] values = credentials.split(":",2);\r
+\r
+                       if (values.length == 2) {\r
+                               String username = values[0];\r
+                               char[] password = values[1].toCharArray();\r
+                               UserModel user = authenticate(username, password);\r
+                               if (user != null) {\r
+                                       flagWicketSession(AuthenticationType.CREDENTIALS);\r
+                                       logger.info(MessageFormat.format("{0} authenticated by BASIC request header from {1}",\r
+                                                       user.username, httpRequest.getRemoteAddr()));\r
+                                       return user;\r
+                               } else {\r
+                                       logger.warn(MessageFormat.format("Failed login attempt for {0}, invalid credentials ({1}) from {2}", \r
+                                                       username, credentials, httpRequest.getRemoteAddr()));\r
+                               }\r
+                       }\r
+               }\r
                return null;\r
        }\r
+       \r
+       protected void flagWicketSession(AuthenticationType authenticationType) {\r
+               RequestCycle requestCycle = RequestCycle.get();\r
+               if (requestCycle != null) {\r
+                       // flag the Wicket session, if this is a Wicket request\r
+                       GitBlitWebSession session = GitBlitWebSession.get();\r
+                       session.authenticationType = authenticationType;\r
+               }\r
+       }\r
 \r
        /**\r
         * Open a file resource using the Servlet container.\r
@@ -693,6 +756,9 @@ public class GitBlit implements ServletContextListener {
         * @return true if successful\r
         */\r
        public boolean deleteUser(String username) {\r
+               if (StringUtils.isEmpty(username)) {\r
+                       return false;\r
+               }\r
                return userService.deleteUser(username);\r
        }\r
 \r
@@ -704,6 +770,9 @@ public class GitBlit implements ServletContextListener {
         * @return a user object or null\r
         */\r
        public UserModel getUserModel(String username) {\r
+               if (StringUtils.isEmpty(username)) {\r
+                       return null;\r
+               }\r
                UserModel user = userService.getUserModel(username);            \r
                return user;\r
        }\r