diff options
author | James Moger <james.moger@gitblit.com> | 2012-12-03 16:59:17 -0500 |
---|---|---|
committer | James Moger <james.moger@gitblit.com> | 2012-12-03 16:59:17 -0500 |
commit | 37fa664c58df034607edf2485a1414b3417b2755 (patch) | |
tree | 206e27ae15c4c7303bf5b3d2277c281e28210f06 | |
parent | 58d93319bd870c93d16764b86a4163ac2d1f0561 (diff) | |
download | gitblit-37fa664c58df034607edf2485a1414b3417b2755.tar.gz gitblit-37fa664c58df034607edf2485a1414b3417b2755.zip |
Consolidate authentication techniques and support container principals (issue-68)
-rw-r--r-- | docs/04_releases.mkd | 6 | ||||
-rw-r--r-- | src/com/gitblit/AuthenticationFilter.java | 43 | ||||
-rw-r--r-- | src/com/gitblit/Constants.java | 2 | ||||
-rw-r--r-- | src/com/gitblit/GitBlit.java | 103 |
4 files changed, 94 insertions, 60 deletions
diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index 3f03160a..bf57d118 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -72,7 +72,7 @@ This is extreme and should be considered carefully since it affects every https #### changes
-- Access restricted servlets (e.g. DownloadZip, RSS, etc) will try to authenticate any Gitblit cookie found in the request before resorting to BASIC authentication.
+- 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.
- Added *groovy* and *scala* to *web.prettyPrintExtensions*
- Added short commit id column to log and history tables (issue 168)
- Teams can now specify the *admin*, *create*, and *fork* roles to simplify user administration
@@ -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)
- LDAP admin attribute setting is now consistent with LDAP teams setting and admin teams list.
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.
+- Support servlet container authentication for existing UserModels (issue 68)
#### dependency changes
-- updated to Jetty 7.6.7
+- updated to Jetty 7.6.8
- updated to JGit 2.1.0.201209190230-r
- updated to Groovy 1.8.8
- updated to Wicket 1.4.21
- updated to Lucene 3.6.1
- updated to BouncyCastle 1.47
+- updated to MarkdownPapers 1.3.2
- added JCalendar 1.3.2
- added Commons-Compress 1.4.1
- added XZ for Java 1.0
diff --git a/src/com/gitblit/AuthenticationFilter.java b/src/com/gitblit/AuthenticationFilter.java index 64aa4411..eb6e95b7 100644 --- a/src/com/gitblit/AuthenticationFilter.java +++ b/src/com/gitblit/AuthenticationFilter.java @@ -16,9 +16,7 @@ package com.gitblit;
import java.io.IOException;
-import java.nio.charset.Charset;
import java.security.Principal;
-import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
@@ -37,7 +35,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import com.gitblit.models.UserModel;
-import com.gitblit.utils.Base64;
import com.gitblit.utils.StringUtils;
/**
@@ -51,9 +48,7 @@ import com.gitblit.utils.StringUtils; */
public abstract class AuthenticationFilter implements Filter {
- protected static final String BASIC = "Basic";
-
- protected static final String CHALLENGE = BASIC + " realm=\"" + Constants.NAME + "\"";
+ protected static final String CHALLENGE = "Basic realm=\"" + Constants.NAME + "\"";
protected static final String SESSION_SECURED = "com.gitblit.secured";
@@ -103,40 +98,8 @@ public abstract class AuthenticationFilter implements Filter { * @return user
*/
protected UserModel getUser(HttpServletRequest httpRequest) {
- UserModel user = null;
- // try request authentication
- user = GitBlit.self().authenticate(httpRequest);
- if (user != null) {
- return user;
- } else if (requiresClientCertificate()) {
- // http request does not have a valid certificate
- // and the filter requires one
- return null;
- }
-
- // look for client authorization credentials in header
- final String authorization = httpRequest.getHeader("Authorization");
- if (authorization != null && authorization.startsWith(BASIC)) {
- // Authorization: Basic base64credentials
- String base64Credentials = authorization.substring(BASIC.length()).trim();
- String credentials = new String(Base64.decode(base64Credentials),
- Charset.forName("UTF-8"));
- // credentials = username:password
- final String[] values = credentials.split(":",2);
-
- if (values.length == 2) {
- String username = values[0];
- char[] password = values[1].toCharArray();
- user = GitBlit.self().authenticate(username, password);
- if (user != null) {
- return user;
- }
- }
- if (GitBlit.isDebugMode()) {
- logger.info(MessageFormat.format("AUTH: invalid credentials ({0})", credentials));
- }
- }
- return null;
+ UserModel user = GitBlit.self().authenticate(httpRequest, requiresClientCertificate());
+ return user;
}
/**
diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index 4669c4c9..d152651b 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -399,7 +399,7 @@ public class Constants { }
public static enum AuthenticationType {
- CREDENTIALS, COOKIE, CERTIFICATE;
+ CREDENTIALS, COOKIE, CERTIFICATE, CONTAINER;
public boolean isStandard() {
return ordinal() <= COOKIE.ordinal();
diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 69135c49..02906ee9 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -24,6 +24,8 @@ import java.io.InputStreamReader; import java.lang.reflect.Field;
import java.net.URI;
import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+import java.security.Principal;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -98,6 +100,7 @@ import com.gitblit.models.SettingModel; import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.Base64;
import com.gitblit.utils.ByteFormat;
import com.gitblit.utils.ContainerUtils;
import com.gitblit.utils.DeepCopier;
@@ -567,6 +570,20 @@ public class GitBlit implements ServletContextListener { * @return a user object or null
*/
public UserModel authenticate(HttpServletRequest httpRequest) {
+ return authenticate(httpRequest, false);
+ }
+
+ /**
+ * Authenticate a user based on HTTP request parameters.
+ *
+ * Authentication by X509Certificate, servlet container principal, cookie,
+ * and BASIC header.
+ *
+ * @param httpRequest
+ * @param requiresCertificate
+ * @return a user object or null
+ */
+ public UserModel authenticate(HttpServletRequest httpRequest, boolean requiresCertificate) {
// try to authenticate by certificate
boolean checkValidity = settings.getBoolean(Keys.git.enforceCertificateValidity, true);
String [] oids = getStrings(Keys.git.certificateUsernameOIDs).toArray(new String[0]);
@@ -574,39 +591,85 @@ public class GitBlit implements ServletContextListener { if (model != null) {
// grab real user model and preserve certificate serial number
UserModel user = getUserModel(model.username);
+ X509Metadata metadata = HttpUtils.getCertificateMetadata(httpRequest);
if (user != null) {
- RequestCycle requestCycle = RequestCycle.get();
- if (requestCycle != null) {
- // flag the Wicket session, if this is a Wicket request
- GitBlitWebSession session = GitBlitWebSession.get();
- session.authenticationType = AuthenticationType.CERTIFICATE;
- }
- X509Metadata metadata = HttpUtils.getCertificateMetadata(httpRequest);
+ flagWicketSession(AuthenticationType.CERTIFICATE);
logger.info(MessageFormat.format("{0} authenticated by client certificate {1} from {2}",
user.username, metadata.serialNumber, httpRequest.getRemoteAddr()));
return user;
+ } else {
+ logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted client certificate ({1}) authentication from {2}",
+ model.username, metadata.serialNumber, httpRequest.getRemoteAddr()));
+ }
+ }
+
+ if (requiresCertificate) {
+ // caller requires client certificate authentication (e.g. git servlet)
+ return null;
+ }
+
+ // try to authenticate by servlet container principal
+ Principal principal = httpRequest.getUserPrincipal();
+ if (principal != null) {
+ UserModel user = getUserModel(principal.getName());
+ if (user != null) {
+ flagWicketSession(AuthenticationType.CONTAINER);
+ logger.info(MessageFormat.format("{0} authenticated by servlet container principal from {1}",
+ user.username, httpRequest.getRemoteAddr()));
+ return user;
+ } else {
+ logger.warn(MessageFormat.format("Failed to find UserModel for {0}, attempted servlet container authentication from {1}",
+ principal.getName(), httpRequest.getRemoteAddr()));
}
}
// try to authenticate by cookie
- Cookie[] cookies = httpRequest.getCookies();
- if (allowCookieAuthentication() && cookies != null && cookies.length > 0) {
- // Grab cookie from Browser Session
- UserModel user = authenticate(cookies);
+ if (allowCookieAuthentication()) {
+ UserModel user = authenticate(httpRequest.getCookies());
if (user != null) {
- RequestCycle requestCycle = RequestCycle.get();
- if (requestCycle != null) {
- // flag the Wicket session, if this is a Wicket request
- GitBlitWebSession session = GitBlitWebSession.get();
- session.authenticationType = AuthenticationType.COOKIE;
- }
+ flagWicketSession(AuthenticationType.COOKIE);
logger.info(MessageFormat.format("{0} authenticated by cookie from {1}",
user.username, httpRequest.getRemoteAddr()));
return user;
}
}
+
+ // try to authenticate by BASIC
+ final String authorization = httpRequest.getHeader("Authorization");
+ if (authorization != null && authorization.startsWith("Basic")) {
+ // Authorization: Basic base64credentials
+ String base64Credentials = authorization.substring("Basic".length()).trim();
+ String credentials = new String(Base64.decode(base64Credentials),
+ Charset.forName("UTF-8"));
+ // credentials = username:password
+ final String[] values = credentials.split(":",2);
+
+ if (values.length == 2) {
+ String username = values[0];
+ char[] password = values[1].toCharArray();
+ UserModel user = authenticate(username, password);
+ if (user != null) {
+ flagWicketSession(AuthenticationType.CREDENTIALS);
+ logger.info(MessageFormat.format("{0} authenticated by BASIC request header from {1}",
+ user.username, httpRequest.getRemoteAddr()));
+ return user;
+ } else {
+ logger.warn(MessageFormat.format("Failed login attempt for {0}, invalid credentials ({1}) from {2}",
+ username, credentials, httpRequest.getRemoteAddr()));
+ }
+ }
+ }
return null;
}
+
+ protected void flagWicketSession(AuthenticationType authenticationType) {
+ RequestCycle requestCycle = RequestCycle.get();
+ if (requestCycle != null) {
+ // flag the Wicket session, if this is a Wicket request
+ GitBlitWebSession session = GitBlitWebSession.get();
+ session.authenticationType = authenticationType;
+ }
+ }
/**
* Open a file resource using the Servlet container.
@@ -693,6 +756,9 @@ public class GitBlit implements ServletContextListener { * @return true if successful
*/
public boolean deleteUser(String username) {
+ if (StringUtils.isEmpty(username)) {
+ return false;
+ }
return userService.deleteUser(username);
}
@@ -704,6 +770,9 @@ public class GitBlit implements ServletContextListener { * @return a user object or null
*/
public UserModel getUserModel(String username) {
+ if (StringUtils.isEmpty(username)) {
+ return null;
+ }
UserModel user = userService.getUserModel(username);
return user;
}
|