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-12537] Incoming RunAsPrincipal is not being propagated to an unsecured EJB #12626

Merged
merged 2 commits into from
Jun 26, 2020

Conversation

Skyllarr
Copy link
Contributor

@wildfly-ci wildfly-ci added the deps-ok Dependencies have been checked, and there are no significant changes label Sep 11, 2019
@@ -643,14 +641,7 @@ private boolean checkCallerSecurityIdentityRole(String roleName) {
}

private SecurityIdentity getCallerSecurityIdentity() {
if (incomingRunAsIdentity != null) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This just reverts the fix for JBEAP-9744. This is now handled by RunAsPrincipalInterceptor.

interceptorFactories.put(InterceptorOrder.View.RUN_AS_PRINCIPAL, new ImmediateInterceptorFactory(new RunAsPrincipalInterceptor(RunAsPrincipalInterceptor.ANONYMOUS_PRINCIPAL, false)));
} else {
// Switch users (set new incoming)
interceptorFactories.put(InterceptorOrder.View.RUN_AS_PRINCIPAL, new ImmediateInterceptorFactory(new RunAsPrincipalInterceptor(runAsPrincipal, isSecurityRequired())));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@darranl We have to add RunAsPrincipalInterceptor to beans that do not have RunAsPrincipal annotation and propagate security. This is to be able to propagate incoming runas identity further. It should behave the same as legacy.

this.securityRequired = securityRequired;
}

private boolean remotingContextIsSet (){
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@darranl Not sure if I should leave this method here or I should create for example SecurityActions class helper for this package or reuse some existing ones (i.e. make them public)?

// run as a user with the given name or if the caller has sufficient permission
if (runAsPrincipal.equals(ANONYMOUS_PRINCIPAL)) {
boolean remotingContextIsSet = remotingContextIsSet();
boolean isRemoteStatelessSecurityDisabled = !(ejbComponent instanceof StatefulSessionComponent) && remotingContextIsSet && !securityRequired;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make principal anonymous and incoming identity null if isRemoteStatelessSecurityDisabled

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if this is the best way to identify stateless bean

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @Skyllarr, you can use a instanceof check against org.jboss.as.ejb3.component.stateless.StatelessSessionComponent to be sure if it's a stateless EJB. In its current form, the above code in the PR, even singleton session bean component will pass this check. I don't know if you want that to happen.
Having said that, I'm just curious - is there a reason why this (security) logic is only applicable for stateless beans?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jaikiran Hello, thanks for review! I changed it to StatelessSessionComponent. This way the identity will not be propagated if the bean is a stateless unsecured remote bean. I did not find any reference for this, but failing tests lead me this way and it also made sense to me that I should not propagate identity for such a bean.

@@ -99,12 +99,4 @@ public void testSwitched() throws Exception {
assertEquals("user1", targetBean.getPrincipalName("user1", "password1"));
}

@Test
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@darranl I don't think this test has valid objective. EntryBean is unsecured stateless remote bean, so its principal is anonymous. This bean should not propagate guest identity to the injected SecurityInformation EJB in my opinion. The returned identity should be anonymous and not guest IMO. I do not think this test should be present for Elytron if the test objective is invalid.

Copy link
Contributor

@fjuma fjuma Dec 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Skyllarr I think it is correct that the injected bean is making use of the identity from the Remoting connection since it is a secured bean.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fjuma I see, after reading #12626 (comment) it probably makes sense to keep it here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fjuma I see, after reading #12626 (comment) it probably makes sense to keep it here.

@Test
public void testNonAnonymousPrincipalInjected(CallerWithIdentity callerWithIdentity) throws Exception {
try {
// Assert.assertEquals(NON_ANONYMOUS_PRINCIPAL, callerWithIdentity.getCallerPrincipalInjected()); TODO see issue WFLY-12538
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nziakova I added your test cases from issue WFLY-11604. I commented out this one because it still needs to be addressed (see https://issues.jboss.org/browse/WFLY-12538)

@darranl
Copy link
Contributor

darranl commented Dec 19, 2019

Retest this please

@darranl
Copy link
Contributor

darranl commented Dec 19, 2019

@tadamski / @chengfang / @fjuma Looks like this one could use some additional review, I am not overly familiar myself with the EJB integration to answer Diana's questions.

@Skyllarr
Copy link
Contributor Author

Retest this please

if(WildFlySecurityManager.isChecking()) {
remoteConnection = WildFlySecurityManager.doUnchecked((PrivilegedAction<RemoteConnection>) () -> RemotingContext.getRemoteConnection());
} else {
remoteConnection = RemotingContext.getRemoteConnection();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RemotingContext class is meant to be used for legacy security, I don't think we should be using it for Elytron.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok it might not be needed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fjuma Ok it might not be needed

if (securityRequired) {
ejbComponentDescription.setSecurityRequired(securityRequired);
}
ejbComponentDescription.setSecurityRequired(securityRequired);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setSecurityRequired should only be called if securityRequired is true (see https://issues.redhat.com/browse/WFLY-12301).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fjuma Ok reverted

@fjuma
Copy link
Contributor

fjuma commented Dec 20, 2019

@Skyllarr I think we should take a step back and see if there is another way to solve this problem. One thing to note is that the meaning of the incomingRunAsIdentity in RunAsPrincipalInterceptor is slightly different for the Elytron case than it was for legacy security. For Elytron, if there is a RunAsPrincipal annotation specified on an EJB, the RunAsPrincipalInterceptor will use securityDomain.createAdHocIdentity(runAsPrincipal) or currentIdentity.createRunAsIdentity(runAsPrincipal) to get the new identity to run as. The interceptor will then call newIdentity.runAs to switch users so this new identity will be used for outgoing calls. The purpose of the incomingRunAsIdentity variable is only to make sure that the RunAsPrincipal annotation doesn't affect the caller's identity (see wildfly-security-incubator@08b5940).

Taking a look at InjectPrincipalTestCase#testNonAnonymousPrincipalInjected:

  • We have a bean with a RunAsPrincipal annotation that has a method that invokes an unsecured bean.
  • When callerWithIdentity.getCallerPrincipalFromEJBContext() is called, we get to the RunAsPrincipalInterceptor, where a newIdentity is correctly created using the "non-anonymous" principal from the RunAsPrincipal annotation. The "non-anonmyous" identity will be used for outgoing calls.
  • The incomingRunAsIdentity is just set to the anonymous identity prior to switching users since that was the current identity when callerWithIdentity.getCallerPrincipalFromEJBContext() was called.
  • When we eventually end up in EJBComponent.getCallerSecurityIdentity, securityDomain.getCurrentSecurityIdentity() will return the "non-anonymous" user. The problem is that because beanB is an unsecured bean, securityRequired evaluates to false so we just return the anonymous identity (see 
    } else if (securityRequired) {
    return securityDomain.getCurrentSecurityIdentity();
    } else {
    // unsecured EJB
    return securityDomain.getAnonymousSecurityIdentity();
    }
    ).

The tricky part is that we do want the anonymous identity to be returned for the Remoting -> unsecured EJB case (as per https://issues.redhat.com/browse/WFLY-8414). So it seems like we only want the "non-anonymous" identity to be returned for the EJB -> unsecured EJB case. Perhaps you could try to see if there is a way to differentiate between these two cases in EJBComponent#getCallerPrincipal so that securityDomain.getCurrentSecurityIdentity() gets returned in the latter case.

@Skyllarr
Copy link
Contributor Author

The tricky part is that we do want the anonymous identity to be returned for the Remoting -> unsecured EJB case (as per https://issues.redhat.com/browse/WFLY-8414).

Is it okay that this would not match the behaviour of legacy? If I understand it correctly legacy would reauthenticate from remoting to unsecured bean and return non-anonymous:

https://github.com/wildfly/wildfly/blob/master/security/subsystem/src/main/java/org/jboss/as/security/service/SimpleSecurityManager.java#L321

So it seems like we only want the "non-anonymous" identity to be returned for the EJB -> unsecured EJB case. Perhaps you could try to see if there is a way to differentiate between these two cases in EJBComponent#getCallerPrincipal so that securityDomain.getCurrentSecurityIdentity() gets returned in the latter case.

So it is important to distinguish if the caller was from EJB or not. I do not see a way to lookup this information in the current code. So maybe we could add it to the security identity (something like boolean isEJB ) and set it in RunAsPrincipalInterceptor to have the appropriate behaviour.

@fjuma
Copy link
Contributor

fjuma commented Jan 28, 2020

The tricky part is that we do want the anonymous identity to be returned for the Remoting -> unsecured EJB case (as per https://issues.redhat.com/browse/WFLY-8414).

Is it okay that this would not match the behaviour of legacy? If I understand it correctly legacy would reauthenticate from remoting to unsecured bean and return non-anonymous:

https://github.com/wildfly/wildfly/blob/master/security/subsystem/src/main/java/org/jboss/as/security/service/SimpleSecurityManager.java#L321

For the Remoting -> unsecured EJB case, the anonymous identity was returned when using legacy security. There's a description of why the anonymous identity gets returned here:

https://issues.redhat.com/browse/JBEAP-9744?focusedCommentId=13399451&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-13399451

@Skyllarr
Copy link
Contributor Author

For the Remoting -> unsecured EJB case, the anonymous identity was returned when using legacy security. There's a description of why the anonymous identity gets returned here:

https://issues.redhat.com/browse/JBEAP-9744?focusedCommentId=13399451&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-13399451

@fjuma Yes sorry you're right.

So it is important to distinguish if the caller was from EJB or not. I do not see a way to lookup this information in the current code. So maybe we could add it to the security identity (something like boolean isEJB ) and set it in RunAsPrincipalInterceptor to have the appropriate behaviour.

@fjuma Do you think it would be good to do it this way?

@fjuma
Copy link
Contributor

fjuma commented Jan 29, 2020

For the Remoting -> unsecured EJB case, the anonymous identity was returned when using legacy security. There's a description of why the anonymous identity gets returned here:
https://issues.redhat.com/browse/JBEAP-9744?focusedCommentId=13399451&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-13399451

@fjuma Yes sorry you're right.

So it is important to distinguish if the caller was from EJB or not. I do not see a way to lookup this information in the current code. So maybe we could add it to the security identity (something like boolean isEJB ) and set it in RunAsPrincipalInterceptor to have the appropriate behaviour.

@fjuma Do you think it would be good to do it this way?

I don't think this should be set in RunAsPrincipalInterceptor as this interceptor is already correctly making sure the correct identity is used for outgoing calls. I think we should take a closer look at EJBComponent#getCallerPrincipal to try to distinguish between the two cases there. It looks like CurrentInvocationContext.get() could be used there to get the interceptor context. I wonder if we could possibly make use of InterceptorContext.getPrivateData() to help us distinguish between the two cases by either trying to make use of existing data or perhaps by adding something new that could be used?

@Skyllarr Skyllarr force-pushed the WFLY-12537 branch 3 times, most recently from c03642b to 971855c Compare January 30, 2020 20:05
@Skyllarr
Copy link
Contributor Author

@fjuma I have updated the PR accordingly. Thanks!

@fjuma
Copy link
Contributor

fjuma commented Feb 10, 2020

Thanks @Skyllarr, this fix looks good to me. I think it would be good to check if the new tests being added here along with the existing test cases now cover the different scenarios (i.e., Remoting -> unsecured EJB, Remoting -> secured EJB, EJB -> unsecured EJB, EJB -> secured EJB) to make sure these are all working properly.

@Skyllarr
Copy link
Contributor Author

Thanks @Skyllarr, this fix looks good to me. I think it would be good to check if the new tests being added here along with the existing test cases now cover the different scenarios (i.e., Remoting -> unsecured EJB, Remoting -> secured EJB, EJB -> unsecured EJB, EJB -> secured EJB) to make sure these are all working properly.

@fjuma Remoting -> secured or unsecured EJB are covered in org.jboss.as.test.integration.ejb.remote.security.RemoteIdentityTestCase. I added test that covers EJB -> secured EJB just now. Thanks

@fjuma
Copy link
Contributor

fjuma commented Feb 18, 2020

Thanks @Skyllarr.

@wildfly-ci wildfly-ci added deps-changed Dependencies have been checked, and there are changes highlighted in a comment and removed deps-ok Dependencies have been checked, and there are no significant changes labels Mar 20, 2020
@Skyllarr
Copy link
Contributor Author

rebased

@wildfly-ci wildfly-ci added deps-ok Dependencies have been checked, and there are no significant changes and removed deps-changed Dependencies have been checked, and there are changes highlighted in a comment labels May 21, 2020
@darranl darranl added the ready-for-merge Only for use by those with merge permissions! label Jun 18, 2020
@bstansberry bstansberry mentioned this pull request Jun 26, 2020
@bstansberry bstansberry merged commit 0a96300 into wildfly:master Jun 26, 2020
@bstansberry
Copy link
Contributor

Thanks @Skyllarr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
deps-ok Dependencies have been checked, and there are no significant changes ready-for-merge Only for use by those with merge permissions!
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants