All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.security.authentication.server.MultiSchemeAuthenticationHandler Maven / Gradle / Ivy

The newest version!
/**
 * 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.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.security.authentication.client.AuthenticationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;

/**
 * The {@link MultiSchemeAuthenticationHandler} supports configuring multiple
 * authentication mechanisms simultaneously. e.g. server can support multiple
 * authentication mechanisms such as Kerberos (SPENGO) and LDAP. During the
 * authentication phase, server will specify all possible authentication schemes
 * and let client choose the appropriate scheme. Please refer to RFC-2616 and
 * HADOOP-12082 for more details.
 * 

* The supported configuration properties are: *

    *
  • multi-scheme-auth-handler.schemes: A comma separated list of HTTP * authentication mechanisms supported by this handler. It does not have a * default value. e.g. multi-scheme-auth-handler.schemes=basic,negotiate *
  • multi-scheme-auth-handler.schemes.${scheme-name}.handler: The * authentication handler implementation to be used for the specified * authentication scheme. It does not have a default value. e.g. * multi-scheme-auth-handler.schemes.negotiate.handler=kerberos *
* * It expected that for every authentication scheme specified in * multi-scheme-auth-handler.schemes property, a handler needs to be configured. * Note that while scheme values in 'multi-scheme-auth-handler.schemes' property * are case-insensitive, the scheme value in the handler configuration property * name must be lower case. i.e. property name such as * multi-scheme-auth-handler.schemes.Negotiate.handler is invalid. */ @InterfaceAudience.Private @InterfaceStability.Evolving public class MultiSchemeAuthenticationHandler implements CompositeAuthenticationHandler { private static Logger logger = LoggerFactory .getLogger(MultiSchemeAuthenticationHandler.class); public static final String SCHEMES_PROPERTY = "multi-scheme-auth-handler.schemes"; public static final String AUTH_HANDLER_PROPERTY = "multi-scheme-auth-handler.schemes.%s.handler"; private static final Splitter STR_SPLITTER = Splitter.on(',').trimResults() .omitEmptyStrings(); private final Map schemeToAuthHandlerMapping = new HashMap<>(); private final Collection types = new HashSet<>(); private final String authType; /** * Constant that identifies the authentication mechanism. */ public static final String TYPE = "multi-scheme"; public MultiSchemeAuthenticationHandler() { this(TYPE); } public MultiSchemeAuthenticationHandler(String authType) { this.authType = authType; } @Override public String getType() { return authType; } /** * This method returns the token types supported by this authentication * handler. * * @return the token types supported by this authentication handler. */ @Override public Collection getTokenTypes() { return types; } @Override public void init(Properties config) throws ServletException { // Useful for debugging purpose. for (Map.Entry prop : config.entrySet()) { logger.info("{} : {}", prop.getKey(), prop.getValue()); } this.types.clear(); String schemesProperty = Preconditions.checkNotNull(config.getProperty(SCHEMES_PROPERTY), "%s system property is not specified.", SCHEMES_PROPERTY); for (String scheme : STR_SPLITTER.split(schemesProperty)) { scheme = AuthenticationHandlerUtil.checkAuthScheme(scheme); if (schemeToAuthHandlerMapping.containsKey(scheme)) { throw new IllegalArgumentException("Handler is already specified for " + scheme + " authentication scheme."); } String authHandlerPropName = String.format(AUTH_HANDLER_PROPERTY, scheme).toLowerCase(); String authHandlerName = config.getProperty(authHandlerPropName); Preconditions.checkNotNull(authHandlerName, "No auth handler configured for scheme %s.", scheme); String authHandlerClassName = AuthenticationHandlerUtil .getAuthenticationHandlerClassName(authHandlerName); AuthenticationHandler handler = initializeAuthHandler(authHandlerClassName, config); schemeToAuthHandlerMapping.put(scheme, handler); types.add(handler.getType()); } logger.info("Successfully initialized MultiSchemeAuthenticationHandler"); } protected AuthenticationHandler initializeAuthHandler( String authHandlerClassName, Properties config) throws ServletException { try { Preconditions.checkNotNull(authHandlerClassName); logger.debug("Initializing Authentication handler of type " + authHandlerClassName); Class klass = Thread.currentThread().getContextClassLoader() .loadClass(authHandlerClassName); AuthenticationHandler authHandler = (AuthenticationHandler) klass.newInstance(); authHandler.init(config); logger.info("Successfully initialized Authentication handler of type " + authHandlerClassName); return authHandler; } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) { logger.error("Failed to initialize authentication handler " + authHandlerClassName, ex); throw new ServletException(ex); } } @Override public void destroy() { for (AuthenticationHandler handler : schemeToAuthHandlerMapping.values()) { handler.destroy(); } } @Override public boolean managementOperation(AuthenticationToken token, HttpServletRequest request, HttpServletResponse response) throws IOException, AuthenticationException { return true; } @Override public AuthenticationToken authenticate(HttpServletRequest request, HttpServletResponse response) throws IOException, AuthenticationException { String authorization = request.getHeader(HttpConstants.AUTHORIZATION_HEADER); if (authorization != null) { for (String scheme : schemeToAuthHandlerMapping.keySet()) { if (AuthenticationHandlerUtil.matchAuthScheme(scheme, authorization)) { AuthenticationHandler handler = schemeToAuthHandlerMapping.get(scheme); AuthenticationToken token = handler.authenticate(request, response); logger.trace("Token generated with type {}", token.getType()); return token; } } } // Handle the case when (authorization == null) or an invalid authorization // header (e.g. a header value without the scheme name). response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); for (String scheme : schemeToAuthHandlerMapping.keySet()) { response.addHeader(HttpConstants.WWW_AUTHENTICATE_HEADER, scheme); } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy