Skip to content

Commit

Permalink
Elytron: make both SwitchIdentityTestCase tests pass
Browse files Browse the repository at this point in the history
  • Loading branch information
tadamski committed Feb 20, 2017
1 parent cbcd558 commit 260372b
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 319 deletions.
Expand Up @@ -44,14 +44,32 @@ class EJBUtil {
// Public methods --------------------------------------------------------

/**
* Lookup for remote EJBs.
* Lookup for remote EJBs using specified ejb client properties.
*
* @param beanImplClass
* @param remoteInterface
* @return
* @throws NamingException
*/
@SuppressWarnings("unchecked")
public static <T> T lookupEJB(Properties ejbClientProperties, Class<? extends T> beanImplClass, Class<T> remoteInterface) throws NamingException {
final Properties jndiProperties = new Properties();
jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
jndiProperties.putAll(ejbClientProperties);
final Context context = new InitialContext(jndiProperties);

return (T) context.lookup("ejb:/" + APPLICATION_NAME + "/" + beanImplClass.getSimpleName() + "!"
+ remoteInterface.getName());
}

/**
* Lookup for remote EJBs.
*
* @param beanImplClass
* @param remoteInterface
* @return
* @throws NamingException
*/
public static <T> T lookupEJB(Class<? extends T> beanImplClass, Class<T> remoteInterface) throws NamingException {
final Hashtable<String, String> jndiProperties = new Hashtable<String, String>();
jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
Expand All @@ -61,7 +79,6 @@ public static <T> T lookupEJB(Class<? extends T> beanImplClass, Class<T> remoteI
return (T) context.lookup("ejb:/" + APPLICATION_NAME + "/" + beanImplClass.getSimpleName() + "!"
+ remoteInterface.getName());
}

/**
* Creates {@link Properties} for the EJB client configuration.
*
Expand All @@ -82,14 +99,13 @@ public static <T> T lookupEJB(Class<? extends T> beanImplClass, Class<T> remoteI
* @return
* @throws UnknownHostException
*/
public static Properties createEjbClientConfiguration(String hostName) throws UnknownHostException {
public static Properties createEjbClientConfiguration(String hostName, String username) throws UnknownHostException {
final Properties pr = new Properties();
pr.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
pr.put("remote.connections", "default");
pr.put("remote.connection.default.host", hostName);
pr.put("remote.connection.default.port", "8080");
pr.put("remote.connection.default.username", CONNECTION_USERNAME);
pr.put("remote.connection.default.password", CONNECTION_PASSWORD);
pr.put("remote.connection.default.username", username);
return pr;
}

Expand Down
Expand Up @@ -21,120 +21,67 @@
*/
package org.jboss.as.test.integration.ejb.container.interceptor.security;

import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ALLOW_RESOURCE_SERVICE_RESTART;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.COMPOSITE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HOST;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OPERATION_HEADERS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PORT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REMOTE_DESTINATION_OUTBOUND_SOCKET_BINDING;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ROLLBACK_ON_RUNTIME_FAILURE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SOCKET_BINDING_GROUP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.STEPS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.ejb.EJBAccessException;
import javax.security.auth.Subject;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;

import org.apache.commons.lang.StringUtils;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.as.arquillian.api.ServerSetup;
import org.jboss.as.arquillian.api.ServerSetupTask;
import org.jboss.as.arquillian.container.ManagementClient;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.test.integration.security.common.AbstractSecurityDomainsServerSetupTask;
import org.jboss.as.test.integration.security.common.AbstractSecurityRealmsServerSetupTask;
import org.jboss.as.test.integration.security.common.Utils;
import org.jboss.as.test.integration.security.common.config.SecurityDomain;
import org.jboss.as.test.integration.security.common.config.SecurityModule;
import org.jboss.as.test.integration.security.common.config.realm.SecurityRealm;
import org.jboss.as.test.integration.security.common.config.realm.ServerIdentity;
import org.jboss.as.test.shared.TestSuiteEnvironment;
import org.jboss.as.test.shared.util.DisableInvocationTestUtil;
import org.jboss.dmr.ModelNode;
import org.jboss.ejb.client.EJBClientContext;
import org.jboss.logging.Logger;
import org.jboss.resteasy.plugins.server.embedded.SimplePrincipal;
import org.jboss.security.ClientLoginModule;
import org.jboss.security.SecurityContextAssociation;
import org.jboss.security.auth.callback.UsernamePasswordHandler;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.ByteArrayAsset;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.wildfly.security.auth.client.AuthenticationConfiguration;
import org.wildfly.security.auth.client.AuthenticationContext;
import org.wildfly.security.auth.client.MatchRule;
import org.xnio.OptionMap;
import org.xnio.Options;
import org.xnio.Property;
import org.xnio.Sequence;

import javax.ejb.EJBAccessException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

/**
* Testcase based on ejb-security-interceptors quickstart application. It tests security context propagation for EJBs.
*
* @author Josef Cacek
* @author <a href="mailto:tadamski@redhat.com">Tomasz Adamski</a>
*/
@RunWith(Arquillian.class)
@ServerSetup({ SwitchIdentityTestCase.SecurityDomainsSetup.class, //
SwitchIdentityTestCase.SecurityRealmsSetup.class, //
SwitchIdentityTestCase.RemotingSetup.class })
SwitchIdentityTestCase.SecurityRealmsSetup.class })
@RunAsClient
public class SwitchIdentityTestCase {

private static final String EJB_OUTBOUND_SOCKET_BINDING = "ejb-outbound";

private static Logger LOGGER = Logger.getLogger(SwitchIdentityTestCase.class);

private static final String EJB_OUTBOUND_REALM = "ejb-outbound-realm";
private static final String SECURITY_DOMAIN_NAME = "switch-identity-test";

private static final PathAddress ADDR_SOCKET_BINDING = PathAddress.pathAddress()
.append(SOCKET_BINDING_GROUP, "standard-sockets")
.append(REMOTE_DESTINATION_OUTBOUND_SOCKET_BINDING, EJB_OUTBOUND_SOCKET_BINDING);
private static final PathAddress ADDR_REMOTING_CONNECTOR = PathAddress.pathAddress().append(SUBSYSTEM, "remoting")
.append("remote-outbound-connection", "ejb-outbound-connection");

@BeforeClass
public static void beforeClass() {
DisableInvocationTestUtil.disable();
}

/**
* The login {@link Configuration} which always returns a single {@link AppConfigurationEntry} with a
* {@link ClientLoginModule}.
*/
private static final Configuration CLIENT_LOGIN_CONFIG = new Configuration() {

@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
final Map<String, String> options = new HashMap<String, String>();
options.put("multi-threaded", "true");
options.put("restore-login-identity", "true");

AppConfigurationEntry clmEntry = new AppConfigurationEntry(ClientLoginModule.class.getName(),
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);

return new AppConfigurationEntry[] { clmEntry };
}
};

@ArquillianResource
private ManagementClient mgmtClient;

Expand All @@ -154,15 +101,9 @@ public static JavaArchive createDeployment() throws IOException {
jar.addAsManifestResource(SwitchIdentityTestCase.class.getPackage(), "jboss-ejb3.xml", "jboss-ejb3.xml");
jar.addAsManifestResource(new StringAsset(ClientSecurityInterceptor.class.getName()),
"services/org.jboss.ejb.client.EJBClientInterceptor");
jar.addAsManifestResource(SwitchIdentityTestCase.class.getPackage(), "jboss-ejb-client.xml", "jboss-ejb-client.xml");
jar.addAsManifestResource(org.jboss.as.test.integration.ejb.container.interceptor.security.api.ClientSecurityInterceptor.class.getPackage(), "permissions.xml", "permissions.xml");
jar.addAsManifestResource(Utils.getJBossDeploymentStructure("org.jboss.remoting3", "org.jboss.as.domain-management",
"org.jboss.as.controller", "org.jboss.as.core-security"), "jboss-deployment-structure.xml");
final Properties props = EJBUtil.createEjbClientConfiguration(StringUtils.strip(
TestSuiteEnvironment.getServerAddress(), "[]"));
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
props.store(baos, null);
jar.addAsResource(new ByteArrayAsset(baos.toByteArray()), "jboss-ejb-client.properties");
return jar;
}

Expand All @@ -185,22 +126,20 @@ public void testSecurityContextAssociation() throws Exception {
*/
@Test
public void testClientLoginModule() throws Exception {
callUsingClientLoginModul("guest", false, false);
callUsingClientLoginModul("user1", true, false);
callUsingClientLoginModul("user2", false, true);
callUsingClientLoginModule("guest", false, false);
callUsingClientLoginModule("user1", true, false);
callUsingClientLoginModule("user2", false, true);
}

// Private methods -------------------------------------------------------

/**
* Perform the tests using the ClientLoginModule and LoginContext API to set the desired Principal.
*/
private void callUsingClientLoginModul(String userName, boolean hasRole1, boolean hasRole2) throws Exception {
LoginContext loginContext = null;
try {
loginContext = new LoginContext("foo", new Subject(), new UsernamePasswordHandler(userName, new char[0]),
CLIENT_LOGIN_CONFIG);
loginContext.login();
private void callUsingClientLoginModule(String userName, boolean hasRole1, boolean hasRole2) throws Exception {

AuthenticationContext authenticationContext = setupAuthenticationContext(userName);
authenticationContext.runCallable(() -> {

// register the client side interceptor
final EJBClientContext ejbClientContext = EJBClientContext.getCurrent().withAddedInterceptors(new ClientSecurityInterceptor());
Expand All @@ -219,29 +158,54 @@ private void callUsingClientLoginModul(String userName, boolean hasRole1, boolea
testMethodAccess(bridgeBean, ManageMethodEnum.ROLE2, hasRole2);
return null;
});
} finally {
if (loginContext != null) {
loginContext.logout();
return null;
});
}

private AuthenticationContext setupAuthenticationContext(final String username) {
OptionMap.Builder builder = OptionMap.builder().set(Options.SASL_POLICY_NOANONYMOUS, true);
builder.set(Options.SASL_POLICY_NOPLAINTEXT, false);
builder.set(Options.SASL_DISALLOWED_MECHANISMS, Sequence.of("JBOSS-LOCAL-USER"));

final AuthenticationContext authenticationContext = AuthenticationContext.empty()
.with(
MatchRule.ALL,
AuthenticationConfiguration.EMPTY
.useName(username == null ? "$local" : username)
.useRealm(null)
.allowSaslMechanisms("DIGEST-MD5")
.useMechanismProperties(getSaslProperties(builder.getMap()))
.useProvidersFromClassLoader(SwitchIdentityTestCase.class.getClassLoader()));

return authenticationContext;
}

private Map<String, String> getSaslProperties(final OptionMap connectionCreationOptions) {
Map<String, String> saslProperties = null;
Sequence<Property> value = connectionCreationOptions.get(Options.SASL_PROPERTIES);
if (value != null) {
saslProperties = new HashMap<>(value.size());
for (Property property : value) {
saslProperties.put(property.getKey(), (String) property.getValue());
}
}
return saslProperties;
}

/**
* Perform the tests using the SecurityContextAssociation API to set the desired Principal.
*/
private void callUsingSecurityContextAssociation(String userName, boolean hasRole1, boolean hasRole2) throws Exception {
try {
final Properties ejbClientConfiguration = EJBUtil.createEjbClientConfiguration(Utils.getHost(mgmtClient));
// TODO Elytron: Once support for legacy EJB properties has been added back, actually set the EJB properties
// that should be used for this test using ejbClientConfiguration
final Properties ejbClientConfiguration = EJBUtil.createEjbClientConfiguration(Utils.getHost(mgmtClient), userName);

// register the client side interceptor
final EJBClientContext ejbClientContext = EJBClientContext.getCurrent().withAddedInterceptors(new ClientSecurityInterceptor());
SecurityContextAssociation.setPrincipal(new SimplePrincipal(userName));

ejbClientContext.runCallable(() -> {
final Manage targetBean = EJBUtil.lookupEJB(TargetBean.class, Manage.class);
final Manage bridgeBean = EJBUtil.lookupEJB(BridgeBean.class, Manage.class);
final Manage targetBean = EJBUtil.lookupEJB(ejbClientConfiguration,TargetBean.class, Manage.class);
final Manage bridgeBean = EJBUtil.lookupEJB(ejbClientConfiguration, BridgeBean.class, Manage.class);

//test direct access
testMethodAccess(targetBean, ManageMethodEnum.ALLROLES, true);
Expand Down Expand Up @@ -366,68 +330,6 @@ protected SecurityRealm[] getSecurityRealms() {
}
}

/**
* A {@link ServerSetupTask} instance which creates remoting mappings for this test case.
*
* @author Josef Cacek
*/
static class RemotingSetup implements ServerSetupTask {

public void setup(ManagementClient managementClient, String containerId) throws Exception {
final List<ModelNode> updates = new LinkedList<ModelNode>();
LOGGER.trace("Adding socket binding");
// /socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=ejb-outbound:add(host=localhost,port=8080) {allow-resource-service-restart=true}
ModelNode socketBindingModelNode = Util.createAddOperation(ADDR_SOCKET_BINDING);
socketBindingModelNode.get(HOST).set(Utils.getHost(managementClient));
socketBindingModelNode.get(PORT).set(8080);
socketBindingModelNode.get(OPERATION_HEADERS, ALLOW_RESOURCE_SERVICE_RESTART).set(true);
updates.add(socketBindingModelNode);

final ModelNode compositeOp = new ModelNode();
compositeOp.get(OP).set(COMPOSITE);
compositeOp.get(OP_ADDR).setEmptyList();
ModelNode steps = compositeOp.get(STEPS);

// /subsystem=remoting/remote-outbound-connection=ejb-outbound-connection:add(outbound-socket-binding-ref=ejb-outbound,username=ConnectionUser,security-realm=ejb-outbound-realm) {allow-resource-service-restart=true}
final ModelNode remotingConnectorModelNode = Util.createAddOperation(ADDR_REMOTING_CONNECTOR);
remotingConnectorModelNode.get("outbound-socket-binding-ref").set(EJB_OUTBOUND_SOCKET_BINDING);
remotingConnectorModelNode.get("username").set(EJBUtil.CONNECTION_PASSWORD);
remotingConnectorModelNode.get("security-realm").set(EJB_OUTBOUND_REALM);
remotingConnectorModelNode.get("protocol").set("http-remoting");
remotingConnectorModelNode.get(OPERATION_HEADERS, ALLOW_RESOURCE_SERVICE_RESTART).set(true);
steps.add(remotingConnectorModelNode);

// /subsystem=remoting/remote-outbound-connection=ejb-outbound-connection/property=SSL_ENABLED:add(value=false)
final ModelNode sslPropertyModelNode = Util.createAddOperation(ADDR_REMOTING_CONNECTOR.append("property",
"SSL_ENABLED"));
sslPropertyModelNode.get(VALUE).set(false);
sslPropertyModelNode.get(OPERATION_HEADERS, ALLOW_RESOURCE_SERVICE_RESTART).set(true);
steps.add(sslPropertyModelNode);

updates.add(compositeOp);
Utils.applyUpdates(updates, managementClient.getControllerClient());

}

public void tearDown(ManagementClient managementClient, String containerId) throws Exception {
final List<ModelNode> updates = new ArrayList<ModelNode>();

// /subsystem=remoting/remote-outbound-connection=ejb-outbound-connection:remove()
ModelNode op = Util.createRemoveOperation(ADDR_REMOTING_CONNECTOR);
op.get(OPERATION_HEADERS, ROLLBACK_ON_RUNTIME_FAILURE).set(false);
op.get(OPERATION_HEADERS, ALLOW_RESOURCE_SERVICE_RESTART).set(true);
updates.add(op);

// /socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=ejb-outbound:remove()
op = Util.createRemoveOperation(ADDR_SOCKET_BINDING);
op.get(OPERATION_HEADERS, ROLLBACK_ON_RUNTIME_FAILURE).set(false);
op.get(OPERATION_HEADERS, ALLOW_RESOURCE_SERVICE_RESTART).set(true);
updates.add(op);

Utils.applyUpdates(updates, managementClient.getControllerClient());
}
}

/**
* An Enum, which holds expected method types in {@link Manage} interface.
*
Expand Down

0 comments on commit 260372b

Please sign in to comment.