Browse Source

implement an HTTP header AuthenticationProvider

tags/v1.8.0
Joel Johnson 9 years ago
parent
commit
46f61d3990

+ 37
- 0
src/main/distrib/data/defaults.properties View File

@@ -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)
#

+ 2
- 2
src/main/java/com/gitblit/Constants.java View File

@@ -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()) {

+ 48
- 5
src/main/java/com/gitblit/auth/AuthenticationProvider.java View File

@@ -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() {

@@ -202,6 +235,11 @@ public abstract class AuthenticationProvider {

}

@Override
public UserModel authenticate(HttpServletRequest httpRequest) {
return null;
}

@Override
public UserModel authenticate(String username, char[] password) {
return null;
@@ -212,6 +250,11 @@ public abstract class AuthenticationProvider {
return AccountType.LOCAL;
}

@Override
public AuthenticationType getAuthenticationType() {
return null;
}

@Override
public boolean supportsCredentialChanges() {
return true;

+ 161
- 0
src/main/java/com/gitblit/auth/HttpHeaderAuthProvider.java View File

@@ -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;
}
}

+ 20
- 2
src/main/java/com/gitblit/manager/AuthenticationManager.java View File

@@ -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;
}

Loading…
Cancel
Save