summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main/distrib/data/defaults.properties37
-rw-r--r--src/main/java/com/gitblit/Constants.java4
-rw-r--r--src/main/java/com/gitblit/auth/AuthenticationProvider.java53
-rw-r--r--src/main/java/com/gitblit/auth/HttpHeaderAuthProvider.java161
-rw-r--r--src/main/java/com/gitblit/manager/AuthenticationManager.java22
5 files changed, 268 insertions, 9 deletions
diff --git a/src/main/distrib/data/defaults.properties b/src/main/distrib/data/defaults.properties
index ce6267a5..403b7417 100644
--- a/src/main/distrib/data/defaults.properties
+++ b/src/main/distrib/data/defaults.properties
@@ -817,6 +817,7 @@ realm.userService = ${baseFolder}/users.conf
# Valid providers are:
#
# htpasswd
+# httpheader
# ldap
# pam
# redmine
@@ -1739,6 +1740,42 @@ realm.pam.serviceName = system-auth
# SINCE 1.3.2
realm.htpasswd.userfile = ${baseFolder}/htpasswd
+# The name of the HTTP header containing the user name to trust as authenticated
+# default: none
+#
+# WARNING: only use this mechanism if your requests are coming from a trusted
+# and secure source such as a self managed reverse proxy!
+#
+# RESTART REQUIRED
+# SINCE 1.7.2
+realm.httpheader.userheader =
+
+# The name of the HTTP header containing the team names of which the user is a member.
+# If this is defined, then only groups from the headers will be available, whereas
+# if this remains undefined, then local groups will be used.
+#
+# This setting requires that you have configured realm.httpheader.userheader.
+#
+# default: none
+#
+# RESTART REQUIRED
+# SINCE 1.7.2
+realm.httpheader.teamheader =
+
+# The regular expression pattern used to separate team names in the team header value
+# default: ,
+#
+# This setting requires that you have configured realm.httpheader.teamheader
+#
+# RESTART REQUIRED
+# SINCE 1.7.2
+realm.httpheader.teamseparator = ,
+
+# Auto-creates user accounts when successfully authenticated based on HTTP headers.
+#
+# SINCE 1.7.2
+realm.httpheader.autoCreateAccounts = false
+
# Restrict the Salesforce user to members of this org.
# default: 0 (i.e. do not check the Org ID)
#
diff --git a/src/main/java/com/gitblit/Constants.java b/src/main/java/com/gitblit/Constants.java
index 4aa8c0ca..e925ee47 100644
--- a/src/main/java/com/gitblit/Constants.java
+++ b/src/main/java/com/gitblit/Constants.java
@@ -574,7 +574,7 @@ public class Constants {
}
public static enum AuthenticationType {
- PUBLIC_KEY, CREDENTIALS, COOKIE, CERTIFICATE, CONTAINER;
+ PUBLIC_KEY, CREDENTIALS, COOKIE, CERTIFICATE, CONTAINER, HTTPHEADER;
public boolean isStandard() {
return ordinal() <= COOKIE.ordinal();
@@ -582,7 +582,7 @@ public class Constants {
}
public static enum AccountType {
- LOCAL, EXTERNAL, CONTAINER, LDAP, REDMINE, SALESFORCE, WINDOWS, PAM, HTPASSWD;
+ LOCAL, EXTERNAL, CONTAINER, LDAP, REDMINE, SALESFORCE, WINDOWS, PAM, HTPASSWD, HTTPHEADER;
public static AccountType fromString(String value) {
for (AccountType type : AccountType.values()) {
diff --git a/src/main/java/com/gitblit/auth/AuthenticationProvider.java b/src/main/java/com/gitblit/auth/AuthenticationProvider.java
index c56c1843..0bfe2351 100644
--- a/src/main/java/com/gitblit/auth/AuthenticationProvider.java
+++ b/src/main/java/com/gitblit/auth/AuthenticationProvider.java
@@ -18,11 +18,14 @@ package com.gitblit.auth;
import java.io.File;
import java.math.BigInteger;
+import javax.servlet.http.HttpServletRequest;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.Constants.AccountType;
import com.gitblit.Constants.Role;
+import com.gitblit.Constants.AuthenticationType;
import com.gitblit.IStoredSettings;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.manager.IUserManager;
@@ -73,6 +76,8 @@ public abstract class AuthenticationProvider {
return serviceName;
}
+ public abstract AuthenticationType getAuthenticationType();
+
protected void setCookie(UserModel user, char [] password) {
// create a user cookie
if (StringUtils.isEmpty(user.cookie) && !ArrayUtils.isEmpty(password)) {
@@ -116,14 +121,32 @@ public abstract class AuthenticationProvider {
public abstract void stop();
+ /**
+ * Used to handle requests for requests for pages requiring authentication.
+ * This allows authentication to occur based on the contents of the request
+ * itself.
+ *
+ * @param httpRequest
+ * @return
+ */
+ public abstract UserModel authenticate(HttpServletRequest httpRequest);
+
+ /**
+ * Used to authentication user/password credentials, both for login form
+ * and HTTP Basic authentication processing.
+ *
+ * @param username
+ * @param password
+ * @return
+ */
public abstract UserModel authenticate(String username, char[] password);
public abstract AccountType getAccountType();
/**
- * Does the user service support changes to credentials?
+ * Returns true if the users's credentials can be changed.
*
- * @return true or false
+ * @return true if the authentication provider supports credential changes
* @since 1.0.0
*/
public abstract boolean supportsCredentialChanges();
@@ -132,7 +155,7 @@ public abstract class AuthenticationProvider {
* Returns true if the user's display name can be changed.
*
* @param user
- * @return true if the user service supports display name changes
+ * @return true if the authentication provider supports display name changes
*/
public abstract boolean supportsDisplayNameChanges();
@@ -140,7 +163,7 @@ public abstract class AuthenticationProvider {
* Returns true if the user's email address can be changed.
*
* @param user
- * @return true if the user service supports email address changes
+ * @return true if the authentication provider supports email address changes
*/
public abstract boolean supportsEmailAddressChanges();
@@ -148,7 +171,7 @@ public abstract class AuthenticationProvider {
* Returns true if the user's team memberships can be changed.
*
* @param user
- * @return true if the user service supports team membership changes
+ * @return true if the authentication provider supports team membership changes
*/
public abstract boolean supportsTeamMembershipChanges();
@@ -180,6 +203,16 @@ public abstract class AuthenticationProvider {
super(serviceName);
}
+ @Override
+ public UserModel authenticate(HttpServletRequest httpRequest) {
+ return null;
+ }
+
+ @Override
+ public AuthenticationType getAuthenticationType() {
+ return AuthenticationType.CREDENTIALS;
+ }
+
@Override
public void stop() {
@@ -203,6 +236,11 @@ public abstract class AuthenticationProvider {
}
@Override
+ public UserModel authenticate(HttpServletRequest httpRequest) {
+ return null;
+ }
+
+ @Override
public UserModel authenticate(String username, char[] password) {
return null;
}
@@ -213,6 +251,11 @@ public abstract class AuthenticationProvider {
}
@Override
+ public AuthenticationType getAuthenticationType() {
+ return null;
+ }
+
+ @Override
public boolean supportsCredentialChanges() {
return true;
}
diff --git a/src/main/java/com/gitblit/auth/HttpHeaderAuthProvider.java b/src/main/java/com/gitblit/auth/HttpHeaderAuthProvider.java
new file mode 100644
index 00000000..3a9c5399
--- /dev/null
+++ b/src/main/java/com/gitblit/auth/HttpHeaderAuthProvider.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2015 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.auth;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitblit.Constants;
+import com.gitblit.Constants.AccountType;
+import com.gitblit.Constants.AuthenticationType;
+import com.gitblit.Constants.Role;
+import com.gitblit.Keys;
+import com.gitblit.models.TeamModel;
+import com.gitblit.models.UserModel;
+import com.gitblit.utils.StringUtils;
+
+public class HttpHeaderAuthProvider extends AuthenticationProvider {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ protected String userHeaderName;
+ protected String teamHeaderName;
+ protected String teamHeaderSeparator;
+
+ public HttpHeaderAuthProvider() {
+ super("httpheader");
+ }
+
+ @Override
+ public void setup() {
+ // Load HTTP header configuration
+ userHeaderName = settings.getString(Keys.realm.httpheader.userheader, null);
+ teamHeaderName = settings.getString(Keys.realm.httpheader.teamheader, null);
+ teamHeaderSeparator = settings.getString(Keys.realm.httpheader.teamseparator, ",");
+
+ if (StringUtils.isEmpty(userHeaderName)) {
+ logger.warn("HTTP Header authentication is enabled, but no header is not defined in " + Keys.realm.httpheader.userheader);
+ }
+ }
+
+ @Override
+ public void stop() {}
+
+
+ @Override
+ public UserModel authenticate(HttpServletRequest httpRequest) {
+ // Try to authenticate using custom HTTP header if user header is defined
+ if (!StringUtils.isEmpty(userHeaderName)) {
+ String headerUserName = httpRequest.getHeader(userHeaderName);
+ if (!StringUtils.isEmpty(headerUserName) && !userManager.isInternalAccount(headerUserName)) {
+ // We have a user, try to load team names as well
+ Set<TeamModel> userTeams = new HashSet<>();
+ if (!StringUtils.isEmpty(teamHeaderName)) {
+ String headerTeamValue = httpRequest.getHeader(teamHeaderName);
+ if (!StringUtils.isEmpty(headerTeamValue)) {
+ String[] headerTeamNames = headerTeamValue.split(teamHeaderSeparator);
+ for (String teamName : headerTeamNames) {
+ teamName = teamName.trim();
+ if (!StringUtils.isEmpty(teamName)) {
+ TeamModel team = userManager.getTeamModel(teamName);
+ if (null == team) {
+ // Create teams here so they can marked with the correct AccountType
+ team = new TeamModel(teamName);
+ team.accountType = AccountType.HTTPHEADER;
+ updateTeam(team);
+ }
+ userTeams.add(team);
+ }
+ }
+ }
+ }
+
+ UserModel user = userManager.getUserModel(headerUserName);
+ if (user != null) {
+ // If team header is provided in request, reset all team memberships, even if resetting to empty set
+ if (!StringUtils.isEmpty(teamHeaderName)) {
+ user.teams.clear();
+ user.teams.addAll(userTeams);
+ }
+ updateUser(user);
+ return user;
+ } else if (settings.getBoolean(Keys.realm.httpheader.autoCreateAccounts, false)) {
+ // auto-create user from HTTP header
+ user = new UserModel(headerUserName.toLowerCase());
+ user.displayName = headerUserName;
+ user.password = Constants.EXTERNAL_ACCOUNT;
+ user.accountType = AccountType.HTTPHEADER;
+ user.teams.addAll(userTeams);
+ updateUser(user);
+ return user;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public UserModel authenticate(String username, char[] password){
+ // Username/password is not supported for HTTP header authentication
+ return null;
+ }
+
+ @Override
+ public AccountType getAccountType() {
+ return AccountType.HTTPHEADER;
+ }
+
+ @Override
+ public AuthenticationType getAuthenticationType() {
+ return AuthenticationType.HTTPHEADER;
+ }
+
+ @Override
+ public boolean supportsCredentialChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsDisplayNameChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsEmailAddressChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsTeamMembershipChanges() {
+ return StringUtils.isEmpty(teamHeaderName);
+ }
+
+ @Override
+ public boolean supportsRoleChanges(UserModel user, Role role) {
+ return true;
+ }
+
+ @Override
+ public boolean supportsRoleChanges(TeamModel team, Role role) {
+ return true;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/com/gitblit/manager/AuthenticationManager.java b/src/main/java/com/gitblit/manager/AuthenticationManager.java
index 7e0b07be..f092bfed 100644
--- a/src/main/java/com/gitblit/manager/AuthenticationManager.java
+++ b/src/main/java/com/gitblit/manager/AuthenticationManager.java
@@ -41,6 +41,7 @@ import com.gitblit.Keys;
import com.gitblit.auth.AuthenticationProvider;
import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider;
import com.gitblit.auth.HtpasswdAuthProvider;
+import com.gitblit.auth.HttpHeaderAuthProvider;
import com.gitblit.auth.LdapAuthProvider;
import com.gitblit.auth.PAMAuthProvider;
import com.gitblit.auth.RedmineAuthProvider;
@@ -92,6 +93,7 @@ public class AuthenticationManager implements IAuthenticationManager {
// map of shortcut provider names
providerNames = new HashMap<String, Class<? extends AuthenticationProvider>>();
providerNames.put("htpasswd", HtpasswdAuthProvider.class);
+ providerNames.put("httpheader", HttpHeaderAuthProvider.class);
providerNames.put("ldap", LdapAuthProvider.class);
providerNames.put("pam", PAMAuthProvider.class);
providerNames.put("redmine", RedmineAuthProvider.class);
@@ -170,7 +172,11 @@ public class AuthenticationManager implements IAuthenticationManager {
}
/**
- * Authenticate a user based on HTTP request parameters.
+ * Used to handle authentication for page requests.
+ *
+ * This allows authentication to occur based on the contents of the request
+ * itself. If no configured @{AuthenticationProvider}s authenticate succesffully,
+ * a request for login will be shown.
*
* Authentication by X509Certificate is tried first and then by cookie.
*
@@ -185,7 +191,7 @@ public class AuthenticationManager implements IAuthenticationManager {
/**
* Authenticate a user based on HTTP request parameters.
*
- * Authentication by servlet container principal, X509Certificate, cookie,
+ * Authentication by custom HTTP header, servlet container principal, X509Certificate, cookie,
* and finally BASIC header.
*
* @param httpRequest
@@ -319,6 +325,18 @@ public class AuthenticationManager implements IAuthenticationManager {
}
}
}
+
+ // Check each configured AuthenticationProvider
+ for (AuthenticationProvider ap : authenticationProviders) {
+ UserModel authedUser = ap.authenticate(httpRequest);
+ if (null != authedUser) {
+ flagRequest(httpRequest, ap.getAuthenticationType(), authedUser.username);
+ logger.debug(MessageFormat.format("{0} authenticated by {1} from {2} for {3}",
+ authedUser.username, ap.getServiceName(), httpRequest.getRemoteAddr(),
+ httpRequest.getPathInfo()));
+ return validateAuthentication(authedUser, ap.getAuthenticationType());
+ }
+ }
return null;
}