001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.security.provider; 019 020import java.lang.reflect.InvocationTargetException; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.Optional; 025import java.util.ServiceLoader; 026import java.util.concurrent.atomic.AtomicReference; 027import java.util.stream.Collectors; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.hbase.HBaseInterfaceAudience; 030import org.apache.hadoop.hbase.security.User; 031import org.apache.hadoop.hbase.util.Pair; 032import org.apache.hadoop.security.token.Token; 033import org.apache.hadoop.security.token.TokenIdentifier; 034import org.apache.yetus.audience.InterfaceAudience; 035import org.apache.yetus.audience.InterfaceStability; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039/** 040 * Accessor for all SaslAuthenticationProvider instances. 041 */ 042@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.AUTHENTICATION) 043@InterfaceStability.Evolving 044public final class SaslClientAuthenticationProviders { 045 private static final Logger LOG = 046 LoggerFactory.getLogger(SaslClientAuthenticationProviders.class); 047 048 public static final String SELECTOR_KEY = "hbase.client.sasl.provider.class"; 049 public static final String EXTRA_PROVIDERS_KEY = "hbase.client.sasl.provider.extras"; 050 051 private static final AtomicReference<SaslClientAuthenticationProviders> PROVIDER_REF = 052 new AtomicReference<>(); 053 054 private final Collection<SaslClientAuthenticationProvider> providers; 055 private final AuthenticationProviderSelector selector; 056 057 /** 058 * Creates a new instance of SaslClientAuthenticationProviders. 059 * @param conf the configuration to use for loading providers and selector 060 */ 061 public SaslClientAuthenticationProviders(Configuration conf) { 062 ServiceLoader<SaslClientAuthenticationProvider> loader = 063 ServiceLoader.load(SaslClientAuthenticationProvider.class, 064 SaslClientAuthenticationProviders.class.getClassLoader()); 065 HashMap<Byte, SaslClientAuthenticationProvider> providerMap = new HashMap<>(); 066 for (SaslClientAuthenticationProvider provider : loader) { 067 addProviderIfNotExists(provider, providerMap); 068 } 069 070 addExplicitProviders(conf, providerMap); 071 072 providers = Collections.unmodifiableCollection(providerMap.values()); 073 074 if (LOG.isTraceEnabled()) { 075 String loadedProviders = providers.stream().map((provider) -> provider.getClass().getName()) 076 .collect(Collectors.joining(", ")); 077 LOG.trace("Found SaslClientAuthenticationProviders {}", loadedProviders); 078 } 079 080 selector = instantiateSelector(conf, providers); 081 } 082 083 /** 084 * Returns the number of providers that have been registered. 085 */ 086 public int getNumRegisteredProviders() { 087 return providers.size(); 088 } 089 090 /** 091 * Returns a singleton instance of {@link SaslClientAuthenticationProviders}. 092 * @deprecated Since 2.5.14 and 2.6.4, will be removed in newer minor release lines. This class 093 * should not be singleton, please do not use it any more. see HBASE-29144 for more 094 * details. 095 */ 096 @Deprecated 097 public static synchronized SaslClientAuthenticationProviders getInstance(Configuration conf) { 098 SaslClientAuthenticationProviders providers = PROVIDER_REF.get(); 099 if (providers == null) { 100 providers = new SaslClientAuthenticationProviders(conf); 101 PROVIDER_REF.set(providers); 102 } 103 104 return providers; 105 } 106 107 /** 108 * Removes the cached singleton instance of {@link SaslClientAuthenticationProviders}. 109 * @deprecated Since 2.5.14 and 2.6.4, will be removed in newer minor release lines. This class 110 * should not be singleton, please do not use it any more. see HBASE-29144 for more 111 * details. 112 */ 113 @Deprecated 114 public static synchronized void reset() { 115 PROVIDER_REF.set(null); 116 } 117 118 /** 119 * Adds the given {@code provider} to the set, only if an equivalent provider does not already 120 * exist in the set. 121 */ 122 static void addProviderIfNotExists(SaslClientAuthenticationProvider provider, 123 HashMap<Byte, SaslClientAuthenticationProvider> providers) { 124 byte code = provider.getSaslAuthMethod().getCode(); 125 SaslClientAuthenticationProvider existingProvider = providers.get(code); 126 if (existingProvider != null) { 127 throw new RuntimeException("Already registered authentication provider with " + code + " " 128 + existingProvider.getClass()); 129 } 130 providers.put(code, provider); 131 } 132 133 /** 134 * Instantiates the ProviderSelector implementation from the provided configuration. 135 */ 136 private static AuthenticationProviderSelector instantiateSelector(Configuration conf, 137 Collection<SaslClientAuthenticationProvider> providers) { 138 Class<? extends AuthenticationProviderSelector> clz = conf.getClass(SELECTOR_KEY, 139 BuiltInProviderSelector.class, AuthenticationProviderSelector.class); 140 try { 141 AuthenticationProviderSelector selector = clz.getConstructor().newInstance(); 142 selector.configure(conf, providers); 143 if (LOG.isTraceEnabled()) { 144 LOG.trace("Loaded ProviderSelector {}", selector.getClass()); 145 } 146 return selector; 147 } catch (InstantiationException | IllegalAccessException | NoSuchMethodException 148 | InvocationTargetException e) { 149 throw new RuntimeException( 150 "Failed to instantiate " + clz + " as the ProviderSelector defined by " + SELECTOR_KEY, e); 151 } 152 } 153 154 /** 155 * Extracts and instantiates authentication providers from the configuration. 156 */ 157 static void addExplicitProviders(Configuration conf, 158 HashMap<Byte, SaslClientAuthenticationProvider> providers) { 159 for (String implName : conf.getStringCollection(EXTRA_PROVIDERS_KEY)) { 160 Class<?> clz; 161 // Load the class from the config 162 try { 163 clz = Class.forName(implName); 164 } catch (ClassNotFoundException e) { 165 LOG.warn("Failed to load SaslClientAuthenticationProvider {}", implName, e); 166 continue; 167 } 168 169 // Make sure it's the right type 170 if (!SaslClientAuthenticationProvider.class.isAssignableFrom(clz)) { 171 LOG.warn("Ignoring SaslClientAuthenticationProvider {} because it is not an instance of" 172 + " SaslClientAuthenticationProvider", clz); 173 continue; 174 } 175 176 // Instantiate it 177 SaslClientAuthenticationProvider provider; 178 try { 179 provider = (SaslClientAuthenticationProvider) clz.getConstructor().newInstance(); 180 } catch (InstantiationException | IllegalAccessException | NoSuchMethodException 181 | InvocationTargetException e) { 182 LOG.warn("Failed to instantiate SaslClientAuthenticationProvider {}", clz, e); 183 continue; 184 } 185 186 // Add it to our set, only if it doesn't conflict with something else we've 187 // already registered. 188 addProviderIfNotExists(provider, providers); 189 } 190 } 191 192 /** 193 * Returns the provider and token pair for SIMPLE authentication. This method is a "hack" while 194 * SIMPLE authentication for HBase does not flow through the SASL codepath. 195 */ 196 public Pair<SaslClientAuthenticationProvider, Token<? extends TokenIdentifier>> 197 getSimpleProvider() { 198 Optional<SaslClientAuthenticationProvider> optional = providers.stream() 199 .filter((p) -> p instanceof SimpleSaslClientAuthenticationProvider).findFirst(); 200 return new Pair<>(optional.get(), null); 201 } 202 203 /** 204 * Chooses the best authentication provider and corresponding token given the HBase cluster 205 * identifier and the user. 206 */ 207 public Pair<SaslClientAuthenticationProvider, Token<? extends TokenIdentifier>> 208 selectProvider(String clusterId, User clientUser) { 209 return selector.selectProvider(clusterId, clientUser); 210 } 211 212 @Override 213 public String toString() { 214 return providers.stream().map((p) -> p.getClass().getName()) 215 .collect(Collectors.joining(", ", "providers=[", "], selector=")) + selector.getClass(); 216 } 217}