apurtell commented on a change in pull request #884: HBASE-23347 Allowable custom authentication methods for RPCs URL: https://github.com/apache/hbase/pull/884#discussion_r360611827
########## File path: hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java ########## @@ -0,0 +1,221 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.hadoop.hbase.security.provider; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Optional; +import java.util.ServiceLoader; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseInterfaceAudience; +import org.apache.hadoop.hbase.util.Pair; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.token.Token; +import org.apache.hadoop.security.token.TokenIdentifier; +import org.apache.yetus.audience.InterfaceAudience; +import org.apache.yetus.audience.InterfaceStability; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Accessor for all SaslAuthenticationProvider instances. + */ [email protected](HBaseInterfaceAudience.AUTHENTICATION) [email protected] +public final class SaslClientAuthenticationProviders { + private static final Logger LOG = LoggerFactory.getLogger( + SaslClientAuthenticationProviders.class); + + public static final String SELECTOR_KEY = "hbase.client.sasl.provider.class"; + public static final String EXTRA_PROVIDERS_KEY = "hbase.client.sasl.provider.extras"; + + private static final AtomicReference<SaslClientAuthenticationProviders> providersRef = + new AtomicReference<>(); + + private final Collection<SaslClientAuthenticationProvider> providers; + private final AuthenticationProviderSelector selector; + + private SaslClientAuthenticationProviders( + Collection<SaslClientAuthenticationProvider> providers, + AuthenticationProviderSelector selector) { + this.providers = providers; + this.selector = selector; + } + + /** + * Returns the number of providers that have been registered. + */ + public int getNumRegisteredProviders() { + return providers.size(); + } + + /** + * Returns a singleton instance of {@link SaslClientAuthenticationProviders}. + */ + public static synchronized SaslClientAuthenticationProviders getInstance(Configuration conf) { + SaslClientAuthenticationProviders providers = providersRef.get(); + if (providers == null) { + providers = instantiate(conf); + providersRef.set(providers); + } + + return providers; + } + + /** + * Removes the cached singleton instance of {@link SaslClientAuthenticationProviders}. + */ + public static synchronized void reset() { + providersRef.set(null); + } + + /** + * Adds the given {@code provider} to the set, only if an equivalent provider does not + * already exist in the set. + */ + static void addProviderIfNotExists(SaslClientAuthenticationProvider provider, + HashMap<Byte,SaslClientAuthenticationProvider> providers) { + Byte code = provider.getSaslAuthMethod().getCode(); + SaslClientAuthenticationProvider existingProvider = providers.get(code); + if (existingProvider != null) { + throw new RuntimeException("Already registered authentication provider with " + code + " " + + existingProvider.getClass()); + } + providers.put(code, provider); + } + + /** + * Instantiates the ProviderSelector implementation from the provided configuration. + */ + static AuthenticationProviderSelector instantiateSelector(Configuration conf, + Collection<SaslClientAuthenticationProvider> providers) { + Class<? extends AuthenticationProviderSelector> clz = conf.getClass( + SELECTOR_KEY, BuiltInProviderSelector.class, AuthenticationProviderSelector.class); + try { + AuthenticationProviderSelector selector = clz.newInstance(); + selector.configure(conf, providers); + if (LOG.isTraceEnabled()) { + LOG.trace("Loaded ProviderSelector {}", selector.getClass()); + } + return selector; + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException("Failed to instantiate " + clz + + " as the ProviderSelector defined by " + SELECTOR_KEY, e); + } + } + + /** + * Extracts and instantiates authentication providers from the configuration. + */ + static void addExplicitProviders(Configuration conf, + HashMap<Byte,SaslClientAuthenticationProvider> providers) { + for(String implName : conf.getStringCollection(EXTRA_PROVIDERS_KEY)) { + Class<?> clz; + // Load the class from the config + try { + clz = Class.forName(implName); + } catch (ClassNotFoundException e) { + LOG.warn("Failed to load SaslClientAuthenticationProvider {}", implName, e); + continue; + } + + // Make sure it's the right type + if (!SaslClientAuthenticationProvider.class.isAssignableFrom(clz)) { + LOG.warn("Ignoring SaslClientAuthenticationProvider {} because it is not an instance of" + + " SaslClientAuthenticationProvider", clz); + continue; + } + + // Instantiate it + SaslClientAuthenticationProvider provider; + try { + provider = (SaslClientAuthenticationProvider) clz.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + LOG.warn("Failed to instantiate SaslClientAuthenticationProvider {}", clz, e); + continue; + } + + // Add it to our set, only if it doesn't conflict with something else we've + // already registered. + addProviderIfNotExists(provider, providers); + } + } + + /** + * Instantiates all client authentication providers and returns an instance of + * {@link SaslClientAuthenticationProviders}. + */ + static SaslClientAuthenticationProviders instantiate(Configuration conf) { + ServiceLoader<SaslClientAuthenticationProvider> loader = + ServiceLoader.load(SaslClientAuthenticationProvider.class); + HashMap<Byte,SaslClientAuthenticationProvider> providerMap = new HashMap<>(); + for (SaslClientAuthenticationProvider provider : loader) { + addProviderIfNotExists(provider, providerMap); + } + + addExplicitProviders(conf, providerMap); + + Collection<SaslClientAuthenticationProvider> providers = Collections.unmodifiableCollection( + providerMap.values()); + + if (LOG.isTraceEnabled()) { + String loadedProviders = providers.stream() + .map((provider) -> provider.getClass().getName()) + .collect(Collectors.joining(", ")); + LOG.trace("Found SaslClientAuthenticationProviders {}", loadedProviders); + } + + AuthenticationProviderSelector selector = instantiateSelector(conf, providers); + return new SaslClientAuthenticationProviders(providers, selector); + } + + /** + * Returns the provider and token pair for SIMPLE authentication. + * + * This method is a "hack" while SIMPLE authentication for HBase does not flow through Review comment: Yeah another rough edge where auth does not always mean SASL. Minor, though ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: [email protected] With regards, Apache Git Services
