package org.infinispan.test.integration.security.embedded;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.infinispan.Cache;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.global.GlobalConfigurationBuilder;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.test.integration.security.tasks.AbstractKrb5ConfServerSetupTask;
import org.infinispan.test.integration.security.tasks.AbstractSecurityDomainsServerSetupTask;
import org.infinispan.test.integration.security.tasks.AbstractSystemPropertiesServerSetupTask;
import org.infinispan.test.integration.security.tasks.AbstractTraceLoggingServerSetupTask;
import org.infinispan.test.integration.security.utils.ApacheDsKrbLdap;
import org.infinispan.test.integration.security.utils.ManagementClientParams;
import org.infinispan.test.integration.security.utils.Utils;
import org.infinispan.transaction.LockingMode;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import org.jboss.arquillian.container.test.api.ContainerController;
import org.jboss.arquillian.container.test.api.Deployer;
import org.jboss.arquillian.container.test.api.OperateOnDeployment;
import org.jboss.arquillian.junit.InSequence;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.as.arquillian.api.ServerSetupTask;
import org.jboss.as.arquillian.container.ManagementClient;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.test.integration.security.common.config.SecurityDomain;
import org.jboss.as.test.integration.security.common.config.SecurityModule;
import org.jboss.security.SecurityConstants;
import org.junit.Test;
import org.wildfly.test.api.Authentication;
/**
* @author <a href="mailto:vjuranek@redhat.com">Vojtech Juranek</a>
* @since 7.0
*/
public abstract class AbstractNodeAuthentication {
protected static final String COORDINATOR_NODE = "node0";
protected static final String COORDINATOR_JGROUSP_CONFIG_MD5 = "jgroups-tcp-sasl-md5-node0.xml";
protected static final String COORDINATOR_JGROUSP_CONFIG_MD5_USER = "jgroups-tcp-sasl-md5-user-node0.xml";
protected static final String JOINING_NODE_JGROUSP_CONFIG_MD5 = "jgroups-tcp-sasl-md5-node1.xml";
protected static final String COORDINATOR_JGROUSP_CONFIG_KRB = "jgroups-tcp-sasl-krb-node0.xml";
protected static final String JOINING_NODE_JGROUSP_CONFIG_KRB = "jgroups-tcp-sasl-krb-node1.xml";
protected static final String JOINING_NODE_JGROUSP_CONFIG_KRB_FAIL = "jgroups-tcp-sasl-krb-node1-fail.xml";
protected static final String CACHE_NAME = "replicatedCache";
protected static final String TEST_ITEM_KEY = "test_key";
protected static final String TEST_ITEM_VALUE = "test_value";
private static final String TRUE = Boolean.TRUE.toString(); // TRUE
private static final Log LOG = LogFactory.getLog(AbstractNodeAuthentication.class);
private final boolean krbProvided;
@ArquillianResource
protected ContainerController controller;
@ArquillianResource
protected Deployer deployer;
protected abstract String getCoordinatorNodeConfig();
protected abstract String getJoiningNodeName();
protected abstract String getJoiningNodeConfig();
public AbstractNodeAuthentication(boolean krbProvided) {
this.krbProvided = krbProvided;
}
protected Cache<String, String> getReplicatedCache(EmbeddedCacheManager manager) throws Exception {
ConfigurationBuilder cacheConfig = new ConfigurationBuilder();
cacheConfig.transaction().lockingMode(LockingMode.PESSIMISTIC);
cacheConfig.invocationBatching().enable();
cacheConfig.jmxStatistics().disable();
cacheConfig.clustering().cacheMode(CacheMode.REPL_SYNC);
manager.defineConfiguration(CACHE_NAME, cacheConfig.build());
Cache<String, String> replicatedCache = manager.getCache(CACHE_NAME);
return replicatedCache;
}
protected EmbeddedCacheManager getCacheManager(String jgrousConfigFile) {
GlobalConfigurationBuilder globalConfig = new GlobalConfigurationBuilder();
globalConfig.globalJmxStatistics().disable();
globalConfig.globalJmxStatistics().mBeanServerLookup(null); //TODO remove once WFLY-3124 is fixed, for now fail JMX registration
globalConfig.transport().defaultTransport().addProperty("configurationFile", jgrousConfigFile);
EmbeddedCacheManager manager = new DefaultCacheManager(globalConfig.build());
return manager;
}
@Test
@InSequence(1)
public void startNodes() throws Exception {
controller.start(COORDINATOR_NODE);
assertTrue(controller.isStarted(COORDINATOR_NODE));
controller.start(getJoiningNodeName());
assertTrue(controller.isStarted(getJoiningNodeName()));
if (krbProvided) {
for (ManagementClientParams params : getManagementClientListParams()) {
ModelControllerClient client = getModelControllerClient(params);
ManagementClient managementClient = new ManagementClient(client, params.getHostname(),
params.getPort(), "http-remoting");
KerberosSystemPropertiesSetupTask.INSTANCE.setup(managementClient, null);
SecurityTraceLoggingServerSetupTask.INSTANCE.setup(managementClient, null);
SecurityDomainsSetupTask.INSTANCE.setup(managementClient, null);
}
}
deployer.deploy(COORDINATOR_NODE);
deployer.deploy(getJoiningNodeName());
}
@Test
@OperateOnDeployment(COORDINATOR_NODE)
@InSequence(2)
public void testCreateItemOnCoordinator() throws Exception {
Cache<String, String> cache = getReplicatedCache(getCacheManager(getCoordinatorNodeConfig()));
cache.put(TEST_ITEM_KEY, TEST_ITEM_VALUE);
assertEquals(TEST_ITEM_VALUE, cache.get(TEST_ITEM_KEY));
}
@Test
@InSequence(3)
//Needs to be overwritten in test class, which should add annotation @OperateOnDeployment(getJoiningNodeName())
public void testReadItemOnJoiningNode() throws Exception {
EmbeddedCacheManager manager = getCacheManager(getJoiningNodeConfig());
Cache<String, String> cache = getReplicatedCache(manager);
assertEquals("Insufficient number of cluster members", 2, manager.getMembers().size());
assertEquals(TEST_ITEM_VALUE, cache.get(TEST_ITEM_KEY));
}
@Test
@InSequence(4)
public void stopJoiningNodes() throws Exception {
deployer.undeploy(getJoiningNodeName());
deployer.undeploy(COORDINATOR_NODE);
if (krbProvided) {
for (ManagementClientParams params : getManagementClientListParams()) {
ModelControllerClient client = getModelControllerClient(params);
ManagementClient managementClient = new ManagementClient(client, params.getHostname(),
params.getPort(), "http-remoting");
KerberosSystemPropertiesSetupTask.INSTANCE.tearDown(managementClient, null);
SecurityTraceLoggingServerSetupTask.INSTANCE.tearDown(managementClient, null);
SecurityDomainsSetupTask.INSTANCE.tearDown(managementClient, null);
}
}
try {
controller.stop(getJoiningNodeName());
} catch (Exception e) {
LOG.warn("Joining node stop failed with %s", e.getCause());
controller.kill(getJoiningNodeName());
}
try {
controller.stop(COORDINATOR_NODE);
} catch (Exception e) {
LOG.warn("Coordinator node stop failed with %s", e.getCause());
controller.kill(COORDINATOR_NODE);
}
assertFalse(controller.isStarted(getJoiningNodeName()));
assertFalse(controller.isStarted(COORDINATOR_NODE));
}
public static ModelControllerClient getModelControllerClient(ManagementClientParams params) {
try {
return ModelControllerClient.Factory.create(
InetAddress.getByName(params.getHostname()),
params.getPort(),
Authentication.getCallbackHandler()
);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
List<ManagementClientParams> getManagementClientListParams() {
List<ManagementClientParams> list = new ArrayList<>();
for (int i = 0; i < 2; i++) {
ManagementClientParams params = new ManagementClientParams(
System.getProperty("node" + i + ".mgmt.addr"), 10090 + 100 * i
);
list.add(i, params);
}
return list;
}
/**
* A Kerberos system-properties server setup task. Sets path to a <code>krb5.conf</code> file and enables Kerberos
* debug messages.
*
* @author <a href="mailto:jcacek@redhat.com">Josef Cacek</a>
* @author <a href="mailto:vchepeli@redhat.com">Vitalii Chepeliuk</a>
*/
static class KerberosSystemPropertiesSetupTask extends AbstractSystemPropertiesServerSetupTask {
public static final KerberosSystemPropertiesSetupTask INSTANCE = new KerberosSystemPropertiesSetupTask();
/**
* Returns "java.security.krb5.conf" and "sun.security.krb5.debug" properties.
*
* @return Kerberos properties
*/
@Override
protected SystemProperty[] getSystemProperties() {
final Map<String, String> map = new HashMap<>();
map.put("java.security.krb5.conf", "${java.io.tmpdir}" + File.separator + "krb5.conf");
map.put("java.security.krb5.debug", TRUE);
map.put(SecurityConstants.DISABLE_SECDOMAIN_OPTION, TRUE);
return mapToSystemProperties(map);
}
}
/**
* A Trace logging server setup task. Sets trace logging for specified packages
*
* @author <a href="mailto:jcacek@redhat.com">Josef Cacek</a>
* @author <a href="mailto:vchepeli@redhat.com">Vitalii Chepeliuk</a>
*/
static class SecurityTraceLoggingServerSetupTask extends AbstractTraceLoggingServerSetupTask {
public static final SecurityTraceLoggingServerSetupTask INSTANCE = new SecurityTraceLoggingServerSetupTask();
@Override
protected Collection<String> getCategories(ManagementClient managementClient, String containerId) {
return Arrays.asList("javax.security", "org.jboss.security", "org.picketbox");
}
}
/**
* A {@link ServerSetupTask} instance which creates security domains for this test case.
*
* @author jcacek@redhat,com
* @author vchepeli@redhat,com
*/
static class SecurityDomainsSetupTask extends AbstractSecurityDomainsServerSetupTask {
public static final String SECURITY_DOMAIN_PREFIX = "krb-";
private static final String KEYTABS_DIR = "${java.io.tmpdir}" + File.separator + "keytabs" + File.separator;
public static final SecurityDomainsSetupTask INSTANCE = new SecurityDomainsSetupTask();
/**
* Returns SecurityDomains configuration for this testcase.
*/
@Override
protected SecurityDomain[] getSecurityDomains() {
final SecurityDomain krbNode0 = getKrbSecurityDomain("node0", KEYTABS_DIR + "jgroups_node0_clustered.keytab", "jgroups/node0/clustered@INFINISPAN.ORG");
final SecurityDomain krbNode1 = getKrbSecurityDomain("node1", KEYTABS_DIR + "jgroups_node1_clustered.keytab", "jgroups/node1/clustered@INFINISPAN.ORG");
final SecurityDomain krbFail = getKrbSecurityDomain("node1-fail", KEYTABS_DIR + "jgroups_node0_fail_clustered.keytab", "jgroups/node1/clustered2@INFINISPAN.ORG");
return new SecurityDomain[]{krbNode0, krbNode1, krbFail};
}
private SecurityDomain getKrbSecurityDomain(String name, String path, String principal) {
SecurityModule.Builder smBuilder = new SecurityModule.Builder();
if (Utils.IBM_JDK) {
smBuilder.name("com.ibm.security.auth.module.Krb5LoginModule").flag("required")
.putOption("useKeytab", path)
.putOption("credsType", "both") //
.putOption("forwardable", TRUE) //
.putOption("proxiable", TRUE) //
.putOption("noAddress", TRUE);
} else {
smBuilder.name("Kerberos").flag("required") //
.putOption("storeKey", "true")
.putOption("useKeyTab", "true")
.putOption("refreshKrb5Config", "true")
.putOption("doNotPrompt", "true")
.putOption("keyTab", path); //
}
smBuilder
.putOption("principal", principal + "@INFINISPAN.ORG")
.putOption("debug", TRUE);
return new SecurityDomain.Builder()
.name(SECURITY_DOMAIN_PREFIX + name).cacheType("default")
.loginModules(
smBuilder.build()
).build();
}
}
/**
* A Kerberos/Ldap server setup task. Starts Kerberos/Ldap server
*
* @author <a href="mailto:jcacek@redhat.com">Josef Cacek</a>
* @author <a href="mailto:vchepeli@redhat.com">Vitalii Chepeliuk</a>
*/
static class KrbLdapServerSetupTask implements ServerSetupTask {
private static ApacheDsKrbLdap krbLdapServer;
private static boolean krbStarted = false;
@Override
public void setup(ManagementClient managementClient, String s) throws Exception {
final String hostname = Utils.getCannonicalHost(managementClient);
System.setProperty("java.security.krb5.conf", System.getProperty("java.io.tmpdir") + File.separator + "krb5.conf");
if (!krbStarted) {
krbLdapServer = new ApacheDsKrbLdap(hostname);
krbLdapServer.start();
krbStarted = true;
}
}
@Override
public void tearDown(ManagementClient managementClient, String s) throws Exception {
if (krbStarted) {
krbLdapServer.stop();
krbStarted = false;
}
}
}
/**
* Generate kerberos keytabs for infinispan users and ldap service
*
* @author <a href="mailto:jcacek@redhat.com">Josef Cacek</a>
* @author <a href="mailto:vchepeli@redhat.com">Vitalii Chepeliuk</a>
*/
static class Krb5ConfServerSetupTask extends AbstractKrb5ConfServerSetupTask {
public static final File NODE0_KEYTAB_FILE = new File(KEYTABS_DIR, "jgroups_node0_clustered.keytab");
public static final File NODE1_KEYTAB_FILE = new File(KEYTABS_DIR, "jgroups_node1_clustered.keytab");
public static final File NODE1_FAIL_KEYTAB_FILE = new File(KEYTABS_DIR, "jgroups_node0_fail_clustered.keytab");
private static boolean keytabsGenerated = false;
@Override
public void setup(ManagementClient managementClient, String containerId) throws Exception {
if (!keytabsGenerated) {
super.setup(managementClient, containerId);
keytabsGenerated = true;
}
}
@Override
public void tearDown(ManagementClient managementClient, String containerId) throws Exception {
if (keytabsGenerated) {
super.tearDown(managementClient, containerId);
keytabsGenerated = false;
}
}
@Override
protected List<UserForKeyTab> kerberosUsers() {
List<UserForKeyTab> users = new ArrayList<>();
users.add(new UserForKeyTab("jgroups/node0/clustered@INFINISPAN.ORG", "node0password", NODE0_KEYTAB_FILE));
users.add(new UserForKeyTab("jgroups/node1/clustered@INFINISPAN.ORG", "node1password", NODE1_KEYTAB_FILE));
users.add(new UserForKeyTab("jgroups/node1/fail/clustered@INFINISPAN.ORG", "failpassword", NODE1_FAIL_KEYTAB_FILE));
return users;
}
}
}