Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WFLY-981 : @RunAs/@RunAsPrincipal are ignored for @Startup/@Singleton bean #5161

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -51,6 +51,7 @@
import org.jboss.as.ejb3.component.session.StatelessWriteReplaceInterceptor;
import org.jboss.as.ejb3.concurrency.ContainerManagedConcurrencyInterceptorFactory;
import org.jboss.as.ejb3.deployment.EjbJarDescription;
import org.jboss.as.ejb3.security.SecurityContextInterceptorFactory;
import org.jboss.as.ejb3.tx.EjbBMTInterceptor;
import org.jboss.as.ejb3.tx.LifecycleCMTTxInterceptor;
import org.jboss.as.ejb3.tx.TimerCMTTxInterceptor;
Expand Down Expand Up @@ -106,7 +107,12 @@ public ComponentConfiguration createConfiguration(final ClassIndex classIndex, f
ComponentConfiguration singletonComponentConfiguration = new ComponentConfiguration(this, classIndex, moduleClassLoader, moduleLoader);
// setup the component create service
singletonComponentConfiguration.setComponentCreateServiceFactory(new SingletonComponentCreateServiceFactory(this.isInitOnStartup(), dependsOn));

getConfigurators().add(new ComponentConfigurator() {
@Override
public void configure(final DeploymentPhaseContext context, final ComponentDescription description, final ComponentConfiguration configuration) throws DeploymentUnitProcessingException {
configuration.addPostConstructInterceptor(new SecurityContextInterceptorFactory(isSecurityEnabled(), false), InterceptorOrder.View.SECURITY_CONTEXT);
}
});
if (getTransactionManagementType().equals(TransactionManagementType.CONTAINER)) {
//we need to add the transaction interceptor to the lifecycle methods
getConfigurators().add(new ComponentConfigurator() {
Expand Down
Expand Up @@ -31,6 +31,7 @@
import org.jboss.as.ee.component.Component;
import org.jboss.as.ee.component.ComponentInterceptorFactory;
import org.jboss.as.ejb3.component.EJBComponent;
import org.jboss.as.security.service.SimpleSecurityManager;
import org.jboss.invocation.Interceptor;
import org.jboss.invocation.InterceptorFactoryContext;
import org.jboss.metadata.javaee.spec.SecurityRolesMetaData;
Expand All @@ -43,9 +44,15 @@ public class SecurityContextInterceptorFactory extends ComponentInterceptorFacto
private static final String DEFAULT_DOMAIN = "other";

private final boolean securityRequired;
private final boolean propagateSecurity;

public SecurityContextInterceptorFactory(final boolean securityRequired) {
this(securityRequired, true);
}

public SecurityContextInterceptorFactory(final boolean securityRequired, final boolean propagateSecurity) {
this.securityRequired = securityRequired;
this.propagateSecurity = propagateSecurity;
}

@Override
Expand All @@ -54,7 +61,12 @@ protected Interceptor create(final Component component, final InterceptorFactory
throw MESSAGES.unexpectedComponent(component, EJBComponent.class);
}
final EJBComponent ejbComponent = (EJBComponent) component;
final ServerSecurityManager securityManager = ejbComponent.getSecurityManager();
final ServerSecurityManager securityManager;
if(propagateSecurity) {
securityManager = ejbComponent.getSecurityManager();
} else {
securityManager = new SimpleSecurityManager((SimpleSecurityManager) ejbComponent.getSecurityManager());
}
final EJBSecurityMetaData securityMetaData = ejbComponent.getSecurityMetaData();
String securityDomain = securityMetaData.getSecurityDomain();
if (securityDomain == null) {
Expand Down
Expand Up @@ -78,6 +78,21 @@
*/
public class SimpleSecurityManager implements ServerSecurityManager {
private ThreadLocalStack<SecurityContext> contexts = new ThreadLocalStack<SecurityContext>();
/**
* Indicates if we propagate previous SecurityContext informations to the current one or not.
* This was introduced for WFLY-981 where we wanted to have a clean SecurityContext using only the Singleton EJB security
* configuration and not what it might have 'inherited' in the execution stack during a Postconstruct method call.
* @see org.jboss.as.ejb3.security.NonPropagatingSecurityContextInterceptorFactory
*/
private boolean propagate = true;

public SimpleSecurityManager() {
}

public SimpleSecurityManager(SimpleSecurityManager delegate) {
this.securityManagement = delegate.securityManagement;
this.propagate = false;
}

private ISecurityManagement securityManagement = null;

Expand Down Expand Up @@ -269,7 +284,7 @@ public void push(final String securityDomain) {
final SecurityContext previous = SecurityContextAssociation.getSecurityContext();
contexts.push(previous);
SecurityContext current = establishSecurityContext(securityDomain);
if (previous != null) {
if (propagate && previous != null) {
current.setSubjectInfo(getSubjectInfo(previous));
current.setIncomingRunAs(previous.getOutgoingRunAs());
}
Expand Down Expand Up @@ -318,7 +333,7 @@ public void push(final String securityDomain, String userName, char[] password,
final SecurityContext previous = SecurityContextAssociation.getSecurityContext();
contexts.push(previous);
SecurityContext current = establishSecurityContext(securityDomain);
if (previous != null) {
if (propagate && previous != null) {
current.setSubjectInfo(getSubjectInfo(previous));
current.setIncomingRunAs(previous.getOutgoingRunAs());
}
Expand Down Expand Up @@ -354,7 +369,7 @@ public void authenticate(final String runAs, final String runAsPrincipal, final
if (runAs != null) {
RunAs runAsIdentity = new RunAsIdentity(runAs, runAsPrincipal, extraRoles);
context.setOutgoingRunAs(runAsIdentity);
} else if (previous != null && previous.getOutgoingRunAs() != null) {
} else if (propagate && previous != null && previous.getOutgoingRunAs() != null) {
// Ensure the propagation continues.
context.setOutgoingRunAs(previous.getOutgoingRunAs());
}
Expand Down
Expand Up @@ -23,15 +23,23 @@

import javax.ejb.EJBAccessException;
import javax.naming.InitialContext;
import org.hamcrest.CoreMatchers;
import org.jboss.arquillian.container.test.api.Deployer;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.OperateOnDeployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.as.arquillian.api.ServerSetup;
import org.jboss.as.test.categories.CommonCriteria;
import org.jboss.as.test.integration.ejb.security.runasprincipal.Caller;
import org.jboss.as.test.integration.ejb.security.runasprincipal.CallerWithIdentity;
import org.jboss.as.test.integration.ejb.security.runasprincipal.SingletonCallerBean;
import org.jboss.as.test.integration.ejb.security.runasprincipal.SingletonBean;
import org.jboss.as.test.integration.ejb.security.runasprincipal.StatelessBBean;
import org.jboss.as.test.integration.ejb.security.runasprincipal.WhoAmI;
import org.jboss.as.test.integration.ejb.security.runasprincipal.transitive.SimpleSingletonBean;
import org.jboss.as.test.integration.ejb.security.runasprincipal.transitive.SingletonStartupBean;
import org.jboss.as.test.integration.ejb.security.runasprincipal.transitive.StatelessSingletonUseBean;
import org.jboss.as.test.integration.security.common.AbstractSecurityDomainSetup;
import org.jboss.as.test.shared.integration.ejb.security.Util;
import org.jboss.logging.Logger;
Expand Down Expand Up @@ -59,21 +67,46 @@
public class RunAsPrincipalTestCase {

private static final Logger log = Logger.getLogger(RunAsPrincipalTestCase.class);
private static final String STARTUP_SINGLETON_DEPLOYMENT = "startup-transitive-singleton";
private static final String DEPLOYMENT = "runasprincipal-test";

@ArquillianResource
public Deployer deployer;

@Deployment(name = STARTUP_SINGLETON_DEPLOYMENT, managed = false, testable = false)
public static Archive<?> runAsStartupTransitiveDeployment() {
// using JavaArchive doesn't work, because of a bug in Arquillian, it only deploys wars properly
final WebArchive war = ShrinkWrap.create(WebArchive.class, STARTUP_SINGLETON_DEPLOYMENT + ".war")
.addClass(WhoAmI.class)
.addClass(StatelessBBean.class)
.addClass(SingletonStartupBean.class)
.addPackage(Assert.class.getPackage())
.addClass(Util.class)
.addClass(Entry.class)
.addClass(RunAsPrincipalTestCase.class)
.addClass(Base64.class)
.addClasses(AbstractSecurityDomainSetup.class, EjbSecurityDomainSetup.class)
.addAsWebInfResource(RunAsPrincipalTestCase.class.getPackage(), "jboss-ejb3.xml", "jboss-ejb3.xml")
.addAsManifestResource(new StringAsset("Dependencies: org.jboss.as.controller-client,org.jboss.dmr\n"), "MANIFEST.MF");
war.addPackage(CommonCriteria.class.getPackage());
return war;
}

@Deployment
public static Archive<?> runAsDeployment() {
// using JavaArchive doesn't work, because of a bug in Arquillian, it only deploys wars properly
final WebArchive war = ShrinkWrap.create(WebArchive.class, "runasprincipal-test.war")
final WebArchive war = ShrinkWrap.create(WebArchive.class, DEPLOYMENT + ".war")
.addPackage(WhoAmI.class.getPackage())
.addClass(SimpleSingletonBean.class)
.addClass(StatelessSingletonUseBean.class)
.addClass(Util.class)
.addClass(Entry.class)
.addClass(RunAsPrincipalTestCase.class)
.addClass(Base64.class)
.addClasses(AbstractSecurityDomainSetup.class, EjbSecurityDomainSetup.class)
.addAsWebInfResource(RunAsPrincipalTestCase.class.getPackage(), "jboss-ejb3.xml", "jboss-ejb3.xml")
.addAsManifestResource(new StringAsset("Dependencies: org.jboss.as.controller-client,org.jboss.dmr\n"),"MANIFEST.MF");
.addAsManifestResource(new StringAsset("Dependencies: org.jboss.as.controller-client,org.jboss.dmr\n"), "MANIFEST.MF");
war.addPackage(CommonCriteria.class.getPackage());
log.info(war.toString(true));
return war;
}

Expand All @@ -82,13 +115,17 @@ private WhoAmI lookupCallerWithIdentity() throws Exception {
}

private WhoAmI lookupSingleCallerWithIdentity() throws Exception {
return (WhoAmI)new InitialContext().lookup("java:module/" + SingletonCallerBean.class.getSimpleName() + "!" + WhoAmI.class.getName());
return (WhoAmI)new InitialContext().lookup("java:module/" + SingletonBean.class.getSimpleName() + "!" + WhoAmI.class.getName());
}

private WhoAmI lookupCaller() throws Exception {
return (WhoAmI)new InitialContext().lookup("java:module/" + Caller.class.getSimpleName() + "!" + WhoAmI.class.getName());
}

private WhoAmI lookupSingletonUseBeanWithIdentity() throws Exception {
return (WhoAmI) new InitialContext().lookup("java:module/" + StatelessSingletonUseBean.class.getSimpleName() + "!" + WhoAmI.class.getName());
}

@Test
public void testJackInABox() throws Exception {
SecurityClient client = SecurityClientFactory.getSecurityClient();
Expand All @@ -104,12 +141,12 @@ public void testJackInABox() throws Exception {
}

@Test
public void testSingletonSecurity() throws Exception {
public void testSingletonPostconstructSecurity() throws Exception {
SecurityClient client = SecurityClientFactory.getSecurityClient();
client.setSimple("user1", "password1");
client.login();
try {
WhoAmI bean = lookupSingleCallerWithIdentity();
WhoAmI bean = lookupSingleCallerWithIdentity();
String actual = bean.getCallerPrincipal();
Assert.assertEquals("Helloween", actual);
} finally {
Expand Down Expand Up @@ -142,4 +179,47 @@ public void testAnonymous() throws Exception {
client.logout();
}
}

@Test
@OperateOnDeployment(STARTUP_SINGLETON_DEPLOYMENT)
public void testStartupSingletonPostconstructSecurityNotPropagating() {
try {
deployer.deploy(STARTUP_SINGLETON_DEPLOYMENT);
Assert.fail("Deployment should fail");
} catch (Exception dex) {
Throwable t = checkEjbException(dex);
log.info("Expected deployment error because the Singleton has nosecurity context per itself", dex.getCause());
Assert.assertThat(t.getMessage(), t.getMessage(), CoreMatchers.containsString("JBAS014502"));
} finally {
deployer.undeploy(STARTUP_SINGLETON_DEPLOYMENT);
}
}

@Test
public void testSingletonPostconstructSecurityNotPropagating() throws Exception {
SecurityClient client = SecurityClientFactory.getSecurityClient();
client.setSimple("user1", "password1");
client.login();
try {
WhoAmI bean = lookupSingletonUseBeanWithIdentity(); //To load the singleton
bean.getCallerPrincipal();
Assert.fail("Deployment should fail");
} catch (Exception dex) {
Throwable t = checkEjbException(dex);
log.info("Expected deployment error because the Singleton has nosecurity context per itself", dex.getCause());
Assert.assertThat(t.getMessage(), t.getMessage(), CoreMatchers.containsString("JBAS014502"));
} finally {
client.logout();
}
}

private Throwable checkEjbException(Throwable ex) {
if (ex instanceof EJBAccessException) {
return ex;
}
if (ex.getCause() != null) {
return checkEjbException(ex.getCause());
}
return ex;
}
}
Expand Up @@ -20,14 +20,17 @@
*/
package org.jboss.as.test.integration.ejb.security.runasprincipal;

import javax.annotation.PostConstruct;
import javax.annotation.security.RolesAllowed;
import javax.annotation.security.RunAs;
import javax.ejb.EJB;
import javax.ejb.Remote;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import org.jboss.as.test.integration.ejb.security.runasprincipal.WhoAmI;
import org.jboss.ejb3.annotation.RunAsPrincipal;
import org.jboss.ejb3.annotation.SecurityDomain;
import org.junit.Assert;

/**
*
Expand All @@ -40,11 +43,19 @@
@RunAs("Admin")
@RunAsPrincipal("Helloween")
@SecurityDomain("other")
public class SingletonCallerBean implements WhoAmI {
public class SingletonBean implements WhoAmI {
@EJB(beanName = "StatelessBBean")
private WhoAmI beanB;

private String principal;

@PostConstruct
public void init() {
principal = beanB.getCallerPrincipal();
Assert.assertEquals("Helloween", principal);
}

public String getCallerPrincipal() {
return beanB.getCallerPrincipal();
return principal;
}
}
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2013 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 library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.jboss.as.test.integration.ejb.security.runasprincipal.transitive;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Remote;
import javax.ejb.Singleton;
import org.jboss.as.test.integration.ejb.security.runasprincipal.WhoAmI;
import org.jboss.ejb3.annotation.SecurityDomain;

/**
*
* @author <a href="mailto:ehugonne@redhat.com">Emmanuel Hugonnet</a> (c) 2013 Red Hat, inc.
*/
@Singleton
@Remote(WhoAmI.class)
@SecurityDomain("other")
public class SimpleSingletonBean implements WhoAmI {

@EJB(beanName = "StatelessBBean")
private WhoAmI beanB;

private String principal;

@PostConstruct
public void init() {
principal = beanB.getCallerPrincipal();
}

public String getCallerPrincipal() {
return principal;
}

}