
com.hazelcast.security.impl.SecurityUtil Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
*
* 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.
*/
package com.hazelcast.security.impl;
import com.hazelcast.config.LoginModuleConfig;
import com.hazelcast.config.LoginModuleConfig.LoginModuleUsage;
import com.hazelcast.config.security.JaasAuthenticationConfig;
import com.hazelcast.config.security.RealmConfig;
import com.hazelcast.logging.ILogger;
import com.hazelcast.logging.Logger;
import com.hazelcast.security.RealmConfigCallback;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.io.File;
import java.io.IOException;
/**
* Helper methods related to Hazelcast security routines.
*/
@SuppressWarnings({"checkstyle:classdataabstractioncoupling", "checkstyle:ClassFanOutComplexity"})
public final class SecurityUtil {
private static final String TEMP_LOGIN_CONTEXT_NAME = "realmConfigLogin";
private static final String FQCN_KRB5LOGINMODULE_SUN = "com.sun.security.auth.module.Krb5LoginModule";
private static final String FQCN_KRB5LOGINMODULE_IBM = "com.ibm.security.auth.module.Krb5LoginModule";
private static final ILogger LOGGER = Logger.getLogger(SecurityUtil.class);
private static final ThreadLocal SECURE_CALL = new ThreadLocal<>();
private SecurityUtil() {
}
/**
* Runs JAAS authentication ({@link LoginContext#login()}) on {@link RealmConfig} with given name retrieved by using given
* {@link CallbackHandler}. Return either the authenticated {@link Subject} when the authentication passes or {@code null}.
*
* @param callbackHandler handler used to retrieve the {@link RealmConfig}
* @param securityRealm name of a security realm to be retrieved by callbackHandler
* @return {@link Subject} when the authentication passes, {@code null} otherwise
*/
public static Subject getRunAsSubject(CallbackHandler callbackHandler, String securityRealm) {
if (securityRealm == null) {
if (LOGGER.isFineEnabled()) {
LOGGER.fine("No RunAs Subject created for callbackHandler=" + callbackHandler + ", realm is not provided");
}
return null;
}
RealmConfigCallback cb = new RealmConfigCallback(securityRealm);
try {
callbackHandler.handle(new Callback[]{cb});
} catch (IOException | UnsupportedCallbackException e) {
LOGGER.info("Unable to retrieve the RealmConfig", e);
return null;
}
return getRunAsSubject(callbackHandler, cb.getRealmConfig());
}
/**
* Runs JAAS authentication ({@link LoginContext#login()}) on given {@link RealmConfig}.
* Return either the authenticated {@link Subject} when the authentication passes or {@code null}.
*
* @param callbackHandler handler used to retrieve the {@link RealmConfig}
* @return {@link Subject} when the authentication passes, {@code null} otherwise
*/
public static Subject getRunAsSubject(CallbackHandler callbackHandler, RealmConfig realmConfig) {
if (realmConfig == null) {
if (LOGGER.isFineEnabled()) {
LOGGER.fine("The realmConfig is not provided.");
}
return null;
}
LoginConfigurationDelegate loginConfiguration = new LoginConfigurationDelegate(realmConfig.asLoginModuleConfigs());
try {
LoginContext lc = new LoginContext(TEMP_LOGIN_CONTEXT_NAME, new Subject(), callbackHandler, loginConfiguration);
lc.login();
return lc.getSubject();
} catch (LoginException e) {
LOGGER.info("Authentication failed.", e);
return null;
}
}
public static RealmConfig createKerberosJaasRealmConfig(String principal, String keytabPath, boolean isInitiator) {
if (keytabPath == null) {
if (LOGGER.isFineEnabled()) {
LOGGER.fine("The keytab path is not provided.");
}
return null;
}
LoginModuleConfig krb5LoginModuleConfig;
if (hasLoginModuleClass(FQCN_KRB5LOGINMODULE_SUN)) {
krb5LoginModuleConfig = new LoginModuleConfig(FQCN_KRB5LOGINMODULE_SUN, LoginModuleUsage.REQUIRED)
.setOrClear("keyTab", keytabPath).setProperty("doNotPrompt", "true")
.setProperty("useKeyTab", "true")
.setProperty("storeKey", "true")
.setProperty("isInitiator", Boolean.toString(isInitiator));
} else if (hasLoginModuleClass(FQCN_KRB5LOGINMODULE_IBM)) {
krb5LoginModuleConfig = new LoginModuleConfig(FQCN_KRB5LOGINMODULE_IBM, LoginModuleUsage.REQUIRED)
.setProperty("useKeytab", new File(keytabPath).toURI().toString())
.setProperty("credsType", isInitiator ? "both" : "acceptor");
} else {
throw new UnsupportedOperationException("No supported Krb5LoginModule was found in the current Java runtime."
+ " The JAAS security realm configurations can't be created automatically."
+ " You have to explicitly configure the realms.");
}
krb5LoginModuleConfig
.setOrClear("principal", principal)
.setProperty("refreshKrb5Config", "true");
RealmConfig kerberosRealmConfig = new RealmConfig()
.setJaasAuthenticationConfig(new JaasAuthenticationConfig().addLoginModuleConfig(krb5LoginModuleConfig));
if (LOGGER.isFineEnabled()) {
LOGGER.fine(
"A helper security realm for Kerberos keytab-based authentication was generated: " + kerberosRealmConfig);
}
return kerberosRealmConfig;
}
private static boolean hasLoginModuleClass(String fcqn) {
try {
Class.forName(fcqn);
return true;
} catch (Throwable t) {
if (LOGGER.isFinestEnabled()) {
LOGGER.finest("Login module class not found: " + fcqn, t);
}
}
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy