\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
- 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
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
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
*/\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
* @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
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
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
* @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
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
* @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
* @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