| /** |
| * 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; |
| } |