blob: dae3b50ad1c7f62386766a34df343dcb6fd9162f [file] [log] [blame]
/**
* 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. See accompanying LICENSE file.
*/
package org.apache.hadoop.security.authentication.server;
import java.io.IOException;
import java.util.Locale;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.hadoop.security.authentication.client.AuthenticationException;
/**
* The {@link AltKerberosAuthenticationHandler} behaves exactly the same way as
* the {@link KerberosAuthenticationHandler}, except that it allows for an
* alternative form of authentication for browsers while still using Kerberos
* for Java access. This is an abstract class that should be subclassed
* to allow a developer to implement their own custom authentication for browser
* access. The alternateAuthenticate method will be called whenever a request
* comes from a browser.
*/
public abstract class AltKerberosAuthenticationHandler
extends KerberosAuthenticationHandler {
/**
* Constant that identifies the authentication mechanism.
*/
public static final String TYPE = "alt-kerberos";
/**
* Constant for the configuration property that indicates which user agents
* are not considered browsers (comma separated)
*/
public static final String NON_BROWSER_USER_AGENTS =
TYPE + ".non-browser.user-agents";
private static final String NON_BROWSER_USER_AGENTS_DEFAULT =
"java,curl,wget,perl";
private String[] nonBrowserUserAgents;
/**
* Returns the authentication type of the authentication handler,
* 'alt-kerberos'.
*
* @return the authentication type of the authentication handler,
* 'alt-kerberos'.
*/
@Override
public String getType() {
return TYPE;
}
@Override
public void init(Properties config) throws ServletException {
super.init(config);
nonBrowserUserAgents = config.getProperty(
NON_BROWSER_USER_AGENTS, NON_BROWSER_USER_AGENTS_DEFAULT)
.split("\\W*,\\W*");
for (int i = 0; i < nonBrowserUserAgents.length; i++) {
nonBrowserUserAgents[i] =
nonBrowserUserAgents[i].toLowerCase(Locale.ENGLISH);
}
}
/**
* It enforces the the Kerberos SPNEGO authentication sequence returning an
* {@link AuthenticationToken} only after the Kerberos SPNEGO sequence has
* completed successfully (in the case of Java access) and only after the
* custom authentication implemented by the subclass in alternateAuthenticate
* has completed successfully (in the case of browser access).
*
* @param request the HTTP client request.
* @param response the HTTP client response.
*
* @return an authentication token if the request is authorized or null
*
* @throws IOException thrown if an IO error occurred
* @throws AuthenticationException thrown if an authentication error occurred
*/
@Override
public AuthenticationToken authenticate(HttpServletRequest request,
HttpServletResponse response)
throws IOException, AuthenticationException {
AuthenticationToken token;
if (isBrowser(request.getHeader("User-Agent"))) {
token = alternateAuthenticate(request, response);
}
else {
token = super.authenticate(request, response);
}
return token;
}
/**
* This method parses the User-Agent String and returns whether or not it
* refers to a browser. If its not a browser, then Kerberos authentication
* will be used; if it is a browser, alternateAuthenticate from the subclass
* will be used.
* <p>
* A User-Agent String is considered to be a browser if it does not contain
* any of the values from alt-kerberos.non-browser.user-agents; the default
* behavior is to consider everything a browser unless it contains one of:
* "java", "curl", "wget", or "perl". Subclasses can optionally override
* this method to use different behavior.
*
* @param userAgent The User-Agent String, or null if there isn't one
* @return true if the User-Agent String refers to a browser, false if not
*/
protected boolean isBrowser(String userAgent) {
if (userAgent == null) {
return false;
}
userAgent = userAgent.toLowerCase(Locale.ENGLISH);
boolean isBrowser = true;
for (String nonBrowserUserAgent : nonBrowserUserAgents) {
if (userAgent.contains(nonBrowserUserAgent)) {
isBrowser = false;
break;
}
}
return isBrowser;
}
/**
* Subclasses should implement this method to provide the custom
* authentication to be used for browsers.
*
* @param request the HTTP client request.
* @param response the HTTP client response.
* @return an authentication token if the request is authorized, or null
* @throws IOException thrown if an IO error occurs
* @throws AuthenticationException thrown if an authentication error occurs
*/
public abstract AuthenticationToken alternateAuthenticate(
HttpServletRequest request, HttpServletResponse response)
throws IOException, AuthenticationException;
}