Repository: cxf Updated Branches: refs/heads/master de09800f0 -> dae46f8c0
Add the ability to the STS LoginModule to disable OnBehalfOf Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/dae46f8c Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/dae46f8c Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/dae46f8c Branch: refs/heads/master Commit: dae46f8c027afb58879d5c305cb6dc0a3a964750 Parents: de09800 Author: Colm O hEigeartaigh <[email protected]> Authored: Tue Jan 6 13:43:17 2015 +0000 Committer: Colm O hEigeartaigh <[email protected]> Committed: Tue Jan 6 13:43:17 2015 +0000 ---------------------------------------------------------------------- .../cxf/ws/security/trust/STSLoginModule.java | 21 ++- .../ws/security/trust/STSTokenValidator.java | 27 +++- .../apache/cxf/systest/sts/jaas/JAASTest.java | 129 ++++++++++++++++++- .../apache/cxf/systest/sts/jaas/DoubleIt.wsdl | 3 + .../apache/cxf/systest/sts/jaas/cxf-service.xml | 21 +++ .../systests/basic/src/test/resources/sts.jaas | 9 ++ 6 files changed, 200 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/dae46f8c/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSLoginModule.java ---------------------------------------------------------------------- diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSLoginModule.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSLoginModule.java index 5d634ab..029c11b 100644 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSLoginModule.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSLoginModule.java @@ -75,12 +75,24 @@ public class STSLoginModule implements LoginModule { /** * Whether we require roles or not from the STS. If this is not set then the * WS-Trust validate binding is used. If it is set then the issue binding is - * used, where the Username + Password credentials are passed via "OnBehalfOf". - * In addition, claims are added to the request for the standard "role" ClaimType. + * used, where the Username + Password credentials are passed via "OnBehalfOf" + * (unless the DISABLE_ON_BEHALF_OF property is set to "true", see below). In addition, + * claims are added to the request for the standard "role" ClaimType. */ public static final String REQUIRE_ROLES = "require.roles"; /** + * Whether to disable passing Username + Password credentials via "OnBehalfOf". If the + * REQUIRE_ROLES property (see above) is set to "true", then the Issue Binding is used + * and the credentials are passed via OnBehalfOf. If this (DISABLE_ON_BEHALF_OF) property + * is set to "true", then the credentials instead are passed through to the + * WS-SecurityPolicy layer and used depending on the security policy of the STS endpoint. + * For example, if the STS endpoint requires a WS-Security UsernameToken, then the + * credentials are inserted here. + */ + public static final String DISABLE_ON_BEHALF_OF = "disable.on.behalf.of"; + + /** * The WSDL Location of the STS */ public static final String WSDL_LOCATION = "wsdl.location"; @@ -123,6 +135,7 @@ public class STSLoginModule implements LoginModule { private Subject subject; private CallbackHandler callbackHandler; private boolean requireRoles; + private boolean disableOnBehalfOf; private String wsdlLocation; private String serviceName; private String endpointName; @@ -140,6 +153,9 @@ public class STSLoginModule implements LoginModule { if (options.containsKey(REQUIRE_ROLES)) { requireRoles = Boolean.parseBoolean((String)options.get(REQUIRE_ROLES)); } + if (options.containsKey(DISABLE_ON_BEHALF_OF)) { + disableOnBehalfOf = Boolean.parseBoolean((String)options.get(DISABLE_ON_BEHALF_OF)); + } if (options.containsKey(WSDL_LOCATION)) { wsdlLocation = (String)options.get(WSDL_LOCATION); } @@ -199,6 +215,7 @@ public class STSLoginModule implements LoginModule { STSTokenValidator validator = new STSTokenValidator(true); validator.setUseIssueBinding(requireRoles); + validator.setUseOnBehalfOf(!disableOnBehalfOf); // Authenticate token try { http://git-wip-us.apache.org/repos/asf/cxf/blob/dae46f8c/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSTokenValidator.java ---------------------------------------------------------------------- diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSTokenValidator.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSTokenValidator.java index f58e12e..da7f06b 100644 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSTokenValidator.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSTokenValidator.java @@ -45,13 +45,18 @@ import org.apache.wss4j.dom.validate.Validator; /** * A WSS4J-based Validator to validate a received WS-Security credential by dispatching - * it to a STS via WS-Trust. The default binding is "validate", but "issue" using "OnBehalfOf" - * is also possible by setting the "useIssueBinding" property. + * it to a STS via WS-Trust. The default binding is "validate", but "issue" is also possible + * by setting the "useIssueBinding" property. In this case, the credentials are sent via + * "OnBehalfOf" unless the "useOnBehalfOf" property is set to "false", in which case the + * credentials are used depending on the security policy of the STS endpoint (e.g. in a + * UsernameToken if this is what the policy requires). Setting "useOnBehalfOf" to "false" + + * "useIssueBinding" to "true" only works for validating UsernameTokens. */ public class STSTokenValidator implements Validator { private STSSamlAssertionValidator samlValidator = new STSSamlAssertionValidator(); private boolean alwaysValidateToSts; private boolean useIssueBinding; + private boolean useOnBehalfOf = true; private STSClient stsClient; private TokenStore tokenStore; @@ -125,11 +130,19 @@ public class STSTokenValidator implements Validator { SecurityToken returnedToken = null; - if (useIssueBinding) { + if (useIssueBinding && useOnBehalfOf) { ElementCallbackHandler callbackHandler = new ElementCallbackHandler(tokenElement); c.setOnBehalfOf(callbackHandler); returnedToken = c.requestSecurityToken(); c.setOnBehalfOf(null); + } else if (useIssueBinding && !useOnBehalfOf && credential.getUsernametoken() != null) { + c.getProperties().put(SecurityConstants.USERNAME, + credential.getUsernametoken().getName()); + c.getProperties().put(SecurityConstants.PASSWORD, + credential.getUsernametoken().getPassword()); + returnedToken = c.requestSecurityToken(); + c.getProperties().remove(SecurityConstants.USERNAME); + c.getProperties().remove(SecurityConstants.PASSWORD); } else { List<SecurityToken> tokens = c.validateSecurityToken(token); returnedToken = tokens.get(0); @@ -214,6 +227,14 @@ public class STSTokenValidator implements Validator { this.useIssueBinding = useIssueBinding; } + public boolean isUseOnBehalfOf() { + return useOnBehalfOf; + } + + public void setUseOnBehalfOf(boolean useOnBehalfOf) { + this.useOnBehalfOf = useOnBehalfOf; + } + public STSClient getStsClient() { return stsClient; } http://git-wip-us.apache.org/repos/asf/cxf/blob/dae46f8c/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/jaas/JAASTest.java ---------------------------------------------------------------------- diff --git a/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/jaas/JAASTest.java b/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/jaas/JAASTest.java index eab5830..cdc2043 100644 --- a/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/jaas/JAASTest.java +++ b/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/jaas/JAASTest.java @@ -205,18 +205,136 @@ public class JAASTest extends AbstractBusClientServerTestBase { } @org.junit.Test + public void testSuccessfulPassthroughInvocation() throws Exception { + + SpringBusFactory bf = new SpringBusFactory(); + URL busFile = JAASTest.class.getResource("cxf-client.xml"); + + Bus bus = bf.createBus(busFile.toString()); + SpringBusFactory.setDefaultBus(bus); + SpringBusFactory.setThreadDefaultBus(bus); + + URL wsdl = JAASTest.class.getResource("DoubleIt.wsdl"); + Service service = Service.create(wsdl, SERVICE_QNAME); + QName portQName = new QName(NAMESPACE, "DoubleItUTPort3"); + DoubleItPortType utPort = + service.getPort(portQName, DoubleItPortType.class); + updateAddressPort(utPort, PORT); + + ((BindingProvider)utPort).getRequestContext().put( + SecurityConstants.USERNAME, "alice"); + ((BindingProvider)utPort).getRequestContext().put( + SecurityConstants.PASSWORD, "clarinet"); + + doubleIt(utPort, 25); + + // Note that the UsernameToken should be cached for the second invocation + doubleIt(utPort, 35); + + ((java.io.Closeable)utPort).close(); + bus.shutdown(true); + } + + @org.junit.Test + public void testUnsuccessfulAuthenticationPassthroughInvocation() throws Exception { + + SpringBusFactory bf = new SpringBusFactory(); + URL busFile = JAASTest.class.getResource("cxf-client.xml"); + + Bus bus = bf.createBus(busFile.toString()); + SpringBusFactory.setDefaultBus(bus); + SpringBusFactory.setThreadDefaultBus(bus); + + URL wsdl = JAASTest.class.getResource("DoubleIt.wsdl"); + Service service = Service.create(wsdl, SERVICE_QNAME); + QName portQName = new QName(NAMESPACE, "DoubleItUTPort3"); + DoubleItPortType utPort = + service.getPort(portQName, DoubleItPortType.class); + updateAddressPort(utPort, PORT); + + ((BindingProvider)utPort).getRequestContext().put( + SecurityConstants.USERNAME, "alice"); + ((BindingProvider)utPort).getRequestContext().put( + SecurityConstants.PASSWORD, "clarinet2"); + + try { + doubleIt(utPort, 25); + fail("Failure expected on an incorrect password"); + } catch (Exception ex) { + // expected + } + + ((java.io.Closeable)utPort).close(); + bus.shutdown(true); + } + + @org.junit.Test + public void testUnsuccessfulAuthorizationPassthroughInvocation() throws Exception { + + SpringBusFactory bf = new SpringBusFactory(); + URL busFile = JAASTest.class.getResource("cxf-client.xml"); + + Bus bus = bf.createBus(busFile.toString()); + SpringBusFactory.setDefaultBus(bus); + SpringBusFactory.setThreadDefaultBus(bus); + + URL wsdl = JAASTest.class.getResource("DoubleIt.wsdl"); + Service service = Service.create(wsdl, SERVICE_QNAME); + QName portQName = new QName(NAMESPACE, "DoubleItUTPort3"); + DoubleItPortType utPort = + service.getPort(portQName, DoubleItPortType.class); + updateAddressPort(utPort, PORT); + + ((BindingProvider)utPort).getRequestContext().put( + SecurityConstants.USERNAME, "bob"); + ((BindingProvider)utPort).getRequestContext().put( + SecurityConstants.PASSWORD, "trombone"); + + try { + doubleIt(utPort, 25); + fail("Failure expected on an incorrect role"); + } catch (Exception ex) { + // expected + } + + ((java.io.Closeable)utPort).close(); + bus.shutdown(true); + } + + @org.junit.Test public void testJAXRSSuccessfulInvocation() throws Exception { - doubleIt("alice", "clarinet", false); + final String address = "https://localhost:" + PORT + "/doubleit/services/doubleit-rs"; + doubleIt("alice", "clarinet", address, false); } @org.junit.Test public void testJAXRSUnsuccessfulAuthentication() throws Exception { - doubleIt("alice", "clarinet2", true); + final String address = "https://localhost:" + PORT + "/doubleit/services/doubleit-rs"; + doubleIt("alice", "clarinet2", address, true); } @org.junit.Test public void testJAXRSUnsuccessfulAuthorization() throws Exception { - doubleIt("bob", "trombone", true); + final String address = "https://localhost:" + PORT + "/doubleit/services/doubleit-rs"; + doubleIt("bob", "trombone", address, true); + } + + @org.junit.Test + public void testJAXRSSuccessfulPassthroughInvocation() throws Exception { + final String address = "https://localhost:" + PORT + "/doubleit/services/doubleit-rs3"; + doubleIt("alice", "clarinet", address, false); + } + + @org.junit.Test + public void testJAXRSUnsuccessfulAuthenticationPassthroughInvocation() throws Exception { + final String address = "https://localhost:" + PORT + "/doubleit/services/doubleit-rs3"; + doubleIt("alice", "clarinet2", address, true); + } + + @org.junit.Test + public void testJAXRSUnsuccessfulAuthorizationPassthroughInvocation() throws Exception { + final String address = "https://localhost:" + PORT + "/doubleit/services/doubleit-rs3"; + doubleIt("bob", "trombone", address, true); } private static void doubleIt(DoubleItPortType port, int numToDouble) { @@ -224,9 +342,10 @@ public class JAASTest extends AbstractBusClientServerTestBase { assertEquals(numToDouble * 2 , resp); } - private static void doubleIt(String username, String password, boolean authFailureExpected) { + private static void doubleIt(String username, String password, + String address, + boolean authFailureExpected) { final String configLocation = "org/apache/cxf/systest/sts/jaas/cxf-client.xml"; - final String address = "https://localhost:" + PORT + "/doubleit/services/doubleit-rs"; final int numToDouble = 25; WebClient client = null; http://git-wip-us.apache.org/repos/asf/cxf/blob/dae46f8c/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/jaas/DoubleIt.wsdl ---------------------------------------------------------------------- diff --git a/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/jaas/DoubleIt.wsdl b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/jaas/DoubleIt.wsdl index 25b6d90..d157fc1 100644 --- a/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/jaas/DoubleIt.wsdl +++ b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/jaas/DoubleIt.wsdl @@ -39,6 +39,9 @@ <wsdl:port name="DoubleItUTPort2" binding="tns:DoubleItUTBinding"> <soap:address location="https://localhost:8081/doubleit/services/doubleitut2"/> </wsdl:port> + <wsdl:port name="DoubleItUTPort3" binding="tns:DoubleItUTBinding"> + <soap:address location="https://localhost:8081/doubleit/services/doubleitut3"/> + </wsdl:port> </wsdl:service> <wsp:Policy wsu:Id="DoubleItUTPolicy"> <wsp:ExactlyOne> http://git-wip-us.apache.org/repos/asf/cxf/blob/dae46f8c/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/jaas/cxf-service.xml ---------------------------------------------------------------------- diff --git a/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/jaas/cxf-service.xml b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/jaas/cxf-service.xml index 52b1bf6..00669a6 100644 --- a/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/jaas/cxf-service.xml +++ b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/jaas/cxf-service.xml @@ -80,6 +80,20 @@ </jaxws:inInterceptors> </jaxws:endpoint> + <bean id="authenticationInterceptor3" class="org.apache.cxf.interceptor.security.JAASLoginInterceptor"> + <property name="contextName" value="sts_standalone_passthrough"/> + </bean> + + <jaxws:endpoint xmlns:s="http://www.example.org/contract/DoubleIt" id="doubleitut3" implementor="org.apache.cxf.systest.sts.jaas.DoubleItPortTypeImpl" endpointName="s:DoubleItUTPort3" serviceName="s:DoubleItService" depends-on="ClientAuthHttpsSettings" address="https://localhost:${testutil.ports.Server}/doubleit/services/doubleitut3" wsdlLocation="org/apache/cxf/systest/sts/jaas/DoubleIt.wsdl"> + <jaxws:properties> + <entry key="ws-security.validate.token" value="false"/> + </jaxws:properties> + <jaxws:inInterceptors> + <ref bean="authenticationInterceptor3"/> + <ref bean="authorizationInterceptor"/> + </jaxws:inInterceptors> + </jaxws:endpoint> + <!-- JAX-RS service --> <jaxrs:server modelRef="classpath:org/apache/cxf/systest/sts/jaas/jaxrs.xml" depends-on="ClientAuthHttpsSettings" address="https://localhost:${testutil.ports.Server}/doubleit/services/doubleit-rs"> <jaxrs:properties> @@ -91,6 +105,13 @@ </jaxrs:inInterceptors> </jaxrs:server> + <jaxrs:server modelRef="classpath:org/apache/cxf/systest/sts/jaas/jaxrs.xml" depends-on="ClientAuthHttpsSettings" address="https://localhost:${testutil.ports.Server}/doubleit/services/doubleit-rs3"> + <jaxrs:inInterceptors> + <ref bean="authenticationInterceptor3"/> + <ref bean="authorizationInterceptor"/> + </jaxrs:inInterceptors> + </jaxrs:server> + <httpj:engine-factory id="ClientAuthHttpsSettings" bus="cxf"> <httpj:engine port="${testutil.ports.Server}"> <httpj:tlsServerParameters> http://git-wip-us.apache.org/repos/asf/cxf/blob/dae46f8c/services/sts/systests/basic/src/test/resources/sts.jaas ---------------------------------------------------------------------- diff --git a/services/sts/systests/basic/src/test/resources/sts.jaas b/services/sts/systests/basic/src/test/resources/sts.jaas index 1ae10af..b9da508 100644 --- a/services/sts/systests/basic/src/test/resources/sts.jaas +++ b/services/sts/systests/basic/src/test/resources/sts.jaas @@ -12,3 +12,12 @@ sts_standalone { ws-security.username="bob" ws-security.password="trombone"; }; + +sts_standalone_passthrough { + org.apache.cxf.ws.security.trust.STSLoginModule required + require.roles="true" + disable.on.behalf.of="true" + wsdl.location="https://localhost:${testutil.ports.STSServer}/SecurityTokenService/Transport?wsdl" + service.name="{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}SecurityTokenService" + endpoint.name="{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}Transport_Port"; +};
