blob: 829498dfd9fe8f4a496de0912d87ab30a2a3f1f6 [file] [log] [blame]
/**
* 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.io.IOException;
import java.lang.reflect.InvocationTargetException;
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.yetus.audience.InterfaceAudience;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@InterfaceAudience.Private
public final class SaslServerAuthenticationProviders {
private static final Logger LOG = LoggerFactory.getLogger(
SaslClientAuthenticationProviders.class);
public static final String EXTRA_PROVIDERS_KEY = "hbase.server.sasl.provider.extras";
private static final AtomicReference<SaslServerAuthenticationProviders> holder =
new AtomicReference<>();
private final HashMap<Byte, SaslServerAuthenticationProvider> providers;
private SaslServerAuthenticationProviders(Configuration conf,
HashMap<Byte, SaslServerAuthenticationProvider> providers) {
this.providers = providers;
}
/**
* Returns the number of registered providers.
*/
public int getNumRegisteredProviders() {
return providers.size();
}
/**
* Returns a singleton instance of {@link SaslServerAuthenticationProviders}.
*/
public static SaslServerAuthenticationProviders getInstance(Configuration conf) {
SaslServerAuthenticationProviders providers = holder.get();
if (null == providers) {
synchronized (holder) {
// Someone else beat us here
providers = holder.get();
if (null != providers) {
return providers;
}
providers = createProviders(conf);
holder.set(providers);
}
}
return providers;
}
/**
* Removes the cached singleton instance of {@link SaslServerAuthenticationProviders}.
*/
public static void reset() {
synchronized (holder) {
holder.set(null);
}
}
/**
* Adds the given provider into the map of providers if a mapping for the auth code does not
* already exist in the map.
*/
static void addProviderIfNotExists(SaslServerAuthenticationProvider provider,
HashMap<Byte,SaslServerAuthenticationProvider> providers) {
final byte newProviderAuthCode = provider.getSaslAuthMethod().getCode();
final SaslServerAuthenticationProvider alreadyRegisteredProvider = providers.get(
newProviderAuthCode);
if (alreadyRegisteredProvider != null) {
throw new RuntimeException("Trying to load SaslServerAuthenticationProvider "
+ provider.getClass() + ", but "+ alreadyRegisteredProvider.getClass()
+ " is already registered with the same auth code");
}
providers.put(newProviderAuthCode, provider);
}
/**
* Adds any providers defined in the configuration.
*/
static void addExtraProviders(Configuration conf,
HashMap<Byte,SaslServerAuthenticationProvider> providers) {
for (String implName : conf.getStringCollection(EXTRA_PROVIDERS_KEY)) {
Class<?> clz;
try {
clz = Class.forName(implName);
} catch (ClassNotFoundException e) {
LOG.warn("Failed to find SaslServerAuthenticationProvider class {}", implName, e);
continue;
}
if (!SaslServerAuthenticationProvider.class.isAssignableFrom(clz)) {
LOG.warn("Server authentication class {} is not an instance of "
+ "SaslServerAuthenticationProvider", clz);
continue;
}
try {
SaslServerAuthenticationProvider provider =
(SaslServerAuthenticationProvider) clz.getConstructor().newInstance();
addProviderIfNotExists(provider, providers);
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException
| InvocationTargetException e) {
LOG.warn("Failed to instantiate {}", clz, e);
}
}
}
/**
* Loads server authentication providers from the classpath and configuration, and then creates
* the SaslServerAuthenticationProviders instance.
*/
static SaslServerAuthenticationProviders createProviders(Configuration conf) {
ServiceLoader<SaslServerAuthenticationProvider> loader =
ServiceLoader.load(SaslServerAuthenticationProvider.class);
HashMap<Byte,SaslServerAuthenticationProvider> providers = new HashMap<>();
for (SaslServerAuthenticationProvider provider : loader) {
addProviderIfNotExists(provider, providers);
}
addExtraProviders(conf, providers);
if (LOG.isTraceEnabled()) {
String loadedProviders = providers.values().stream()
.map((provider) -> provider.getClass().getName())
.collect(Collectors.joining(", "));
if (loadedProviders.isEmpty()) {
loadedProviders = "None!";
}
LOG.trace("Found SaslServerAuthenticationProviders {}", loadedProviders);
}
// Initialize the providers once, before we get into the RPC path.
providers.forEach((b,provider) -> {
try {
// Give them a copy, just to make sure there is no funny-business going on.
provider.init(new Configuration(conf));
} catch (IOException e) {
LOG.error("Failed to initialize {}", provider.getClass(), e);
throw new RuntimeException(
"Failed to initialize " + provider.getClass().getName(), e);
}
});
return new SaslServerAuthenticationProviders(conf, providers);
}
/**
* Selects the appropriate SaslServerAuthenticationProvider from those available. If there is no
* matching provider for the given {@code authByte}, this method will return null.
*/
public SaslServerAuthenticationProvider selectProvider(byte authByte) {
return providers.get(Byte.valueOf(authByte));
}
/**
* Extracts the SIMPLE authentication provider.
*/
public SaslServerAuthenticationProvider getSimpleProvider() {
Optional<SaslServerAuthenticationProvider> opt = providers.values()
.stream()
.filter((p) -> p instanceof SimpleSaslServerAuthenticationProvider)
.findFirst();
if (!opt.isPresent()) {
throw new RuntimeException("SIMPLE authentication provider not available when it should be");
}
return opt.get();
}
}