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.io.IOException; 021import java.lang.reflect.InvocationTargetException; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.Map; 025import java.util.Optional; 026import java.util.ServiceLoader; 027import java.util.concurrent.atomic.AtomicReference; 028import java.util.stream.Collectors; 029import org.apache.hadoop.conf.Configuration; 030import org.apache.yetus.audience.InterfaceAudience; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034@InterfaceAudience.Private 035public final class SaslServerAuthenticationProviders { 036 private static final Logger LOG = 037 LoggerFactory.getLogger(SaslClientAuthenticationProviders.class); 038 039 public static final String EXTRA_PROVIDERS_KEY = "hbase.server.sasl.provider.extras"; 040 041 private static final AtomicReference<SaslServerAuthenticationProviders> HOLDER = 042 new AtomicReference<>(); 043 044 private final Map<Byte, SaslServerAuthenticationProvider> providers; 045 046 /** 047 * Creates a new instance of SaslServerAuthenticationProviders. 048 * @param conf the configuration to use for loading providers 049 */ 050 public SaslServerAuthenticationProviders(Configuration conf) { 051 ServiceLoader<SaslServerAuthenticationProvider> loader = 052 ServiceLoader.load(SaslServerAuthenticationProvider.class); 053 HashMap<Byte, SaslServerAuthenticationProvider> providerMap = new HashMap<>(); 054 for (SaslServerAuthenticationProvider provider : loader) { 055 addProviderIfNotExists(provider, providerMap); 056 } 057 058 addExtraProviders(conf, providerMap); 059 060 if (LOG.isTraceEnabled()) { 061 String loadedProviders = providerMap.values().stream() 062 .map((provider) -> provider.getClass().getName()).collect(Collectors.joining(", ")); 063 if (loadedProviders.isEmpty()) { 064 loadedProviders = "None!"; 065 } 066 LOG.trace("Found SaslServerAuthenticationProviders {}", loadedProviders); 067 } 068 069 // Initialize the providers once, before we get into the RPC path. 070 providerMap.forEach((b, provider) -> { 071 try { 072 // Give them a copy, just to make sure there is no funny-business going on. 073 provider.init(new Configuration(conf)); 074 } catch (IOException e) { 075 LOG.error("Failed to initialize {}", provider.getClass(), e); 076 throw new RuntimeException("Failed to initialize " + provider.getClass().getName(), e); 077 } 078 }); 079 this.providers = Collections.unmodifiableMap(providerMap); 080 } 081 082 /** 083 * Returns the number of registered providers. 084 */ 085 public int getNumRegisteredProviders() { 086 return providers.size(); 087 } 088 089 /** 090 * Returns a singleton instance of {@link SaslServerAuthenticationProviders}. 091 * @deprecated Since 2.5.14 and 2.6.4, will be removed in newer minor release lines. This class 092 * should not be singleton, please do not use it any more. see HBASE-29144 for more 093 * details. 094 */ 095 @Deprecated 096 public static SaslServerAuthenticationProviders getInstance(Configuration conf) { 097 SaslServerAuthenticationProviders providers = HOLDER.get(); 098 if (null == providers) { 099 synchronized (HOLDER) { 100 // Someone else beat us here 101 providers = HOLDER.get(); 102 if (null != providers) { 103 return providers; 104 } 105 106 providers = new SaslServerAuthenticationProviders(conf); 107 HOLDER.set(providers); 108 } 109 } 110 return providers; 111 } 112 113 /** 114 * Removes the cached singleton instance of {@link SaslServerAuthenticationProviders}. 115 * @deprecated Since 2.5.14 and 2.6.4, will be removed in newer minor release lines. This class 116 * should not be singleton, please do not use it any more. see HBASE-29144 for more 117 * details. 118 */ 119 @Deprecated 120 public static void reset() { 121 synchronized (HOLDER) { 122 HOLDER.set(null); 123 } 124 } 125 126 /** 127 * Adds the given provider into the map of providers if a mapping for the auth code does not 128 * already exist in the map. 129 */ 130 static void addProviderIfNotExists(SaslServerAuthenticationProvider provider, 131 HashMap<Byte, SaslServerAuthenticationProvider> providers) { 132 final byte newProviderAuthCode = provider.getSaslAuthMethod().getCode(); 133 final SaslServerAuthenticationProvider alreadyRegisteredProvider = 134 providers.get(newProviderAuthCode); 135 if (alreadyRegisteredProvider != null) { 136 throw new RuntimeException("Trying to load SaslServerAuthenticationProvider " 137 + provider.getClass() + ", but " + alreadyRegisteredProvider.getClass() 138 + " is already registered with the same auth code"); 139 } 140 providers.put(newProviderAuthCode, provider); 141 } 142 143 /** 144 * Adds any providers defined in the configuration. 145 */ 146 static void addExtraProviders(Configuration conf, 147 HashMap<Byte, SaslServerAuthenticationProvider> providers) { 148 for (String implName : conf.getStringCollection(EXTRA_PROVIDERS_KEY)) { 149 Class<?> clz; 150 try { 151 clz = Class.forName(implName); 152 } catch (ClassNotFoundException e) { 153 LOG.warn("Failed to find SaslServerAuthenticationProvider class {}", implName, e); 154 continue; 155 } 156 157 if (!SaslServerAuthenticationProvider.class.isAssignableFrom(clz)) { 158 LOG.warn("Server authentication class {} is not an instance of " 159 + "SaslServerAuthenticationProvider", clz); 160 continue; 161 } 162 163 try { 164 SaslServerAuthenticationProvider provider = 165 (SaslServerAuthenticationProvider) clz.getConstructor().newInstance(); 166 addProviderIfNotExists(provider, providers); 167 } catch (InstantiationException | IllegalAccessException | NoSuchMethodException 168 | InvocationTargetException e) { 169 LOG.warn("Failed to instantiate {}", clz, e); 170 } 171 } 172 } 173 174 /** 175 * Selects the appropriate SaslServerAuthenticationProvider from those available. If there is no 176 * matching provider for the given {@code authByte}, this method will return null. 177 */ 178 public SaslServerAuthenticationProvider selectProvider(byte authByte) { 179 return providers.get(Byte.valueOf(authByte)); 180 } 181 182 /** 183 * Extracts the SIMPLE authentication provider. 184 */ 185 public SaslServerAuthenticationProvider getSimpleProvider() { 186 Optional<SaslServerAuthenticationProvider> opt = providers.values().stream() 187 .filter((p) -> p instanceof SimpleSaslServerAuthenticationProvider).findFirst(); 188 if (!opt.isPresent()) { 189 throw new RuntimeException("SIMPLE authentication provider not available when it should be"); 190 } 191 return opt.get(); 192 } 193}