/*
* JBoss, Home of Professional Open Source.
* Copyright (c) 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.test.integration.ejb.security;
import java.util.HashMap;
import java.util.Map;
import javax.ejb.EJBAccessException;
import javax.naming.Context;
import javax.naming.NamingException;
import org.jboss.as.arquillian.api.ContainerResource;
import org.jboss.as.arquillian.container.ManagementClient;
import org.jboss.as.test.categories.CommonCriteria;
import org.jboss.as.test.integration.ejb.security.authorization.AnnOnlyCheckSFSBForInjection;
import org.jboss.as.test.integration.ejb.security.authorization.AnnOnlyCheckSLSBForInjection;
import org.jboss.as.test.integration.ejb.security.authorization.ParentAnnOnlyCheck;
import org.jboss.as.test.integration.ejb.security.authorization.SimpleAuthorizationRemote;
import org.jboss.as.test.shared.integration.ejb.security.Util;
import org.jboss.as.test.shared.util.AssumeTestGroupUtil;
import org.jboss.logging.Logger;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.wildfly.security.auth.client.AuthenticationConfiguration;
import org.wildfly.security.auth.client.AuthenticationContext;
import org.wildfly.security.auth.client.MatchRule;
import org.wildfly.security.auth.principal.AnonymousPrincipal;
import org.xnio.OptionMap;
import org.xnio.Options;
import org.xnio.Property;
import org.xnio.Sequence;
/**
* This is a common parent for test cases to check whether basic EJB authorization works from an EJB client to a remote EJB.
*
* @author <a href="mailto:jan.lanik@redhat.com">Jan Lanik</a>
*/
public abstract class AnnSBTest {
@ContainerResource
private ManagementClient managementClient;
@BeforeClass
public static void beforeClass() {
AssumeTestGroupUtil.assumeElytronProfileTestsEnabled();
}
public static Archive<JavaArchive> testAppDeployment(final Logger LOG, final String MODULE, final Class SB_TO_TEST) {
final JavaArchive jar = ShrinkWrap.create(JavaArchive.class, MODULE + ".jar")
.addClass(SB_TO_TEST)
.addClass(SimpleAuthorizationRemote.class)
.addClass(ParentAnnOnlyCheck.class)
.addClass(AnnOnlyCheckSLSBForInjection.class)
.addClass(AnnOnlyCheckSFSBForInjection.class);
jar.addAsManifestResource(AnnSBTest.class.getPackage(), "jboss-ejb3.xml", "jboss-ejb3.xml");
jar.addPackage(CommonCriteria.class.getPackage());
return jar;
}
private SimpleAuthorizationRemote getBean(final String MODULE, final Logger log, final Class SB_CLASS, Context ctx) throws NamingException {
String myContext = Util.createRemoteEjbJndiContext(
"",
MODULE,
"",
SB_CLASS.getSimpleName(),
SimpleAuthorizationRemote.class.getName(),
isBeanClassStatefull(SB_CLASS));
log.trace("JNDI name=" + myContext);
return (SimpleAuthorizationRemote) ctx.lookup(myContext);
}
/**
* Test objective:
* Check if default, @RolesAllowed, @PermitAll, @DenyAll and @RolesAllowed with multiple roles
* works on method level without user logged in as described in EJB 3.1 spec.
* The target session bean is given as parameter
* Expected results:
* Test has to finish without any exception or error.
*
* @throws Exception
*/
public void testSingleMethodAnnotationsNoUserTemplate(final String MODULE, final Logger log, final Class SB_CLASS) throws Exception {
final Context ctx = Util.createNamingContext();
final AuthenticationContext authenticationContext = AuthenticationContext.empty().with(MatchRule.ALL, AuthenticationConfiguration.EMPTY.useAuthorizationPrincipal(AnonymousPrincipal.getInstance()));
authenticationContext.runCallable(() -> {
String echoValue = getBean(MODULE, log, SB_CLASS, ctx).defaultAccess("alohomora");
Assert.assertEquals(echoValue, "alohomora");
try {
echoValue = getBean(MODULE, log, SB_CLASS, ctx).roleBasedAccessOne("alohomora");
Assert.fail("Method cannot be successfully called without logged in user");
} catch (Exception e) {
// expected
Assert.assertTrue("Thrown exception must be EJBAccessException, but was " + e.getClass().getSimpleName(), e instanceof EJBAccessException);
}
try {
echoValue = getBean(MODULE, log, SB_CLASS, ctx).roleBasedAccessMore("alohomora");
Assert.fail("Method cannot be successfully called without logged in user");
} catch (EJBAccessException e) {
// expected
}
try {
echoValue = getBean(MODULE, log, SB_CLASS, ctx).permitAll("alohomora");
Assert.assertEquals(echoValue, "alohomora");
} catch (Exception e) {
Assert.fail("@PermitAll annotation must allow all users and no users to call the method");
}
try {
echoValue = getBean(MODULE, log, SB_CLASS, ctx).denyAll("alohomora");
Assert.fail("@DenyAll annotation must allow all users and no users to call the method");
} catch (Exception e) {
// expected
Assert.assertTrue("Thrown exception must be EJBAccessException, but was " + e.getClass().getSimpleName(), e instanceof EJBAccessException);
}
return null;
});
}
/**
* Test objective:
* Check if default, @RolesAllowed, @PermitAll, @DenyAll and @RolesAllowed with multiple roles
* works on method level with user1 logged in as described in EJB 3.1 spec.
* user1 has "Users,Role1" roles.
* The target session bean is given as parameter.
* Expected results:
* Test has to finish without any exception or error.
* <p/>
*
* @throws Exception
*/
public void testSingleMethodAnnotationsUser1Template(final String MODULE, final Logger log, final Class SB_CLASS) throws Exception {
final Context ctx = Util.createNamingContext();
final AuthenticationContext authenticationContext = setupAuthenticationContext("user1", "password1");
authenticationContext.runCallable(() -> {
try {
String echoValue = getBean(MODULE, log, SB_CLASS, ctx).defaultAccess("alohomora");
Assert.assertEquals(echoValue, "alohomora");
} catch (EJBAccessException e) {
Assert.fail("EJBAccessException not expected");
}
try {
String echoValue = getBean(MODULE, log, SB_CLASS, ctx).roleBasedAccessOne("alohomora");
Assert.assertEquals(echoValue, "alohomora");
} catch (EJBAccessException e) {
Assert.fail("EJBAccessException not expected");
}
try {
String echoValue = getBean(MODULE, log, SB_CLASS, ctx).roleBasedAccessMore("alohomora");
Assert.fail("Method cannot be successfully called with logged in principal.");
} catch (Exception e) {
// expected
Assert.assertTrue("Thrown exception must be EJBAccessException, but was different", e instanceof EJBAccessException);
}
try {
String echoValue = getBean(MODULE, log, SB_CLASS, ctx).permitAll("alohomora");
Assert.assertEquals(echoValue, "alohomora");
} catch (Exception e) {
Assert.fail("@PermitAll annotation must allow all users and no users to call the method - principal.");
}
try {
String echoValue = getBean(MODULE, log, SB_CLASS, ctx).denyAll("alohomora");
Assert.fail("@DenyAll annotation must allow all users and no users to call the method");
} catch (Exception e) {
// expected
Assert.assertTrue("Thrown exception must be EJBAccessException, but was different", e instanceof EJBAccessException);
}
try {
String echoValue = getBean(MODULE, log, SB_CLASS, ctx).starRoleAllowed("alohomora");
Assert.assertEquals(echoValue, "alohomora");
} catch (Exception e) {
Assert.fail(
"@RolesAllowed(\"**\") annotation must allow all authenticated users to the method.");
}
return null;
});
}
/**
* Test objective:
* Check if default, @RolesAllowed, @PermitAll, @DenyAll and @RolesAllowed with multiple roles
* works on method level with user1 logged in as described in EJB 3.1 spec.
* user2 has "Users,Role2" roles.
* The target session bean is given as parameter.
* Expected results:
* Test has to finish without any exception or error.
* <p/>
*
* @throws Exception
*/
public void testSingleMethodAnnotationsUser2Template(final String MODULE, final Logger log, final Class SB_CLASS) throws Exception {
final Context ctx = Util.createNamingContext();
final AuthenticationContext authenticationContext = setupAuthenticationContext("user2", "password2");
authenticationContext.runCallable(() -> {
try {
String echoValue = getBean(MODULE, log, SB_CLASS, ctx).defaultAccess("alohomora");
Assert.assertEquals(echoValue, "alohomora");
} catch (EJBAccessException e) {
Assert.fail("EJBAccessException not expected");
}
try {
String echoValue = getBean(MODULE, log, SB_CLASS, ctx).roleBasedAccessOne("alohomora");
Assert.fail("Method cannot be successfully called with logged in user2");
} catch (Exception e) {
// expected
Assert.assertTrue("Thrown exception must be EJBAccessException, but was different", e instanceof EJBAccessException);
}
try {
String echoValue = getBean(MODULE, log, SB_CLASS, ctx).roleBasedAccessMore("alohomora");
Assert.assertEquals(echoValue, "alohomora");
} catch (EJBAccessException e) {
Assert.fail("EJBAccessException not expected");
}
try {
String echoValue = getBean(MODULE, log, SB_CLASS, ctx).permitAll("alohomora");
Assert.assertEquals(echoValue, "alohomora");
} catch (Exception e) {
Assert.fail("@PermitAll annotation must allow all users and no users to call the method - principal.");
}
try {
String echoValue = getBean(MODULE, log, SB_CLASS, ctx).denyAll("alohomora");
Assert.fail("@DenyAll annotation must allow all users and no users to call the method");
} catch (Exception e) {
// expected
Assert.assertTrue("Thrown exception must be EJBAccessException, but was different", e instanceof EJBAccessException);
}
return null;
});
}
protected AuthenticationContext setupAuthenticationContext(String username, String password) {
OptionMap.Builder builder = OptionMap.builder().set(Options.SASL_POLICY_NOANONYMOUS, true);
builder.set(Options.SASL_POLICY_NOPLAINTEXT, false);
if (password != null) {
builder.set(Options.SASL_DISALLOWED_MECHANISMS, Sequence.of("JBOSS-LOCAL-USER"));
} else {
builder.set(Options.SASL_MECHANISMS, Sequence.of("JBOSS-LOCAL-USER"));
}
final AuthenticationContext authenticationContext = AuthenticationContext.empty()
.with(
MatchRule.ALL,
AuthenticationConfiguration.EMPTY
.useName(username == null ? "$local" : username)
.usePassword(password)
.useRealm(null)
.allowSaslMechanisms(password != null ? "DIGEST-MD5" : "JBOSS-LOCAL-USER")
.useMechanismProperties(getSaslProperties(builder.getMap()))
.useProvidersFromClassLoader(AnnSBTest.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;
}
protected static boolean isBeanClassStatefull(Class bean) {
if (bean.getName().contains("toSLSB")) {
return false;
} else if (bean.getName().contains("toSFSB")) {
return true;
} else if (bean.getName().contains("SFSB")) {
return true;
} else if (bean.getName().contains("SLSB")) {
return false;
} else {
throw new AssertionError("Session bean class has a wrong name!: " + bean.getCanonicalName());
}
}
}