/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.cxf.systest.sts.custom; import java.net.URL; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.security.auth.callback.CallbackHandler; import javax.ws.rs.core.Response; import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; import javax.xml.transform.dom.DOMSource; import javax.xml.ws.BindingProvider; import javax.xml.ws.Service; import org.w3c.dom.Element; import org.apache.cxf.Bus; import org.apache.cxf.bus.spring.SpringBusFactory; import org.apache.cxf.jaxrs.client.WebClient; import org.apache.cxf.rt.security.SecurityConstants; import org.apache.cxf.staxutils.W3CDOMStreamWriter; import org.apache.cxf.systest.sts.common.SecurityTestUtil; import org.apache.cxf.systest.sts.common.TokenTestUtils; import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase; import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType; import org.apache.cxf.ws.security.sts.provider.model.RequestedSecurityTokenType; import org.apache.cxf.ws.security.trust.STSClient; import org.apache.cxf.ws.security.trust.STSUtils; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.crypto.CryptoFactory; import org.apache.wss4j.common.saml.SamlAssertionWrapper; import org.apache.wss4j.common.util.DOM2Writer; import org.apache.wss4j.dom.WSDocInfo; import org.apache.wss4j.dom.engine.WSSecurityEngineResult; import org.apache.wss4j.dom.handler.RequestData; import org.apache.wss4j.dom.processor.Processor; import org.apache.wss4j.dom.processor.SAMLTokenProcessor; import org.example.contract.doubleit.DoubleItPortType; import org.junit.BeforeClass; /** * This test sends a custom parameter indicating the "realm" of the user, which is interpreted by the * STS's CustomUTValidator. */ public class CustomParameterTest extends AbstractBusClientServerTestBase { static final String STSPORT = allocatePort(STSServer.class); private static final String NAMESPACE = "http://www.example.org/contract/DoubleIt"; private static final QName SERVICE_QNAME = new QName(NAMESPACE, "DoubleItService"); private static final String PORT = allocatePort(Server.class); private static final String SAML2_TOKEN_TYPE = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"; @BeforeClass public static void startServers() throws Exception { assertTrue( "Server failed to launch", // run the server in the same process // set this to false to fork launchServer(Server.class, true) ); assertTrue( "Server failed to launch", // run the server in the same process // set this to false to fork launchServer(STSServer.class, true) ); } @org.junit.AfterClass public static void cleanup() throws Exception { SecurityTestUtil.cleanup(); stopAllServers(); } // Here the custom parameter in the RST is parsed by the CustomUTValidator @org.junit.Test public void testCustomParameterInRSTValidator() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = CustomParameterTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); URL wsdl = CustomParameterTest.class.getResource("DoubleIt.wsdl"); Service service = Service.create(wsdl, SERVICE_QNAME); QName portQName = new QName(NAMESPACE, "DoubleItTransportCustomParameterPort"); DoubleItPortType transportClaimsPort = service.getPort(portQName, DoubleItPortType.class); updateAddressPort(transportClaimsPort, PORT); TokenTestUtils.updateSTSPort((BindingProvider)transportClaimsPort, STSPORT); STSClient stsClient = new STSClient(bus); stsClient.setWsdlLocation("https://localhost:" + STSPORT + "/SecurityTokenService/UT?wsdl"); stsClient.setServiceName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}SecurityTokenService"); stsClient.setEndpointName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}UT_Port"); Map<String, Object> properties = new HashMap<>(); properties.put(SecurityConstants.USERNAME, "alice"); properties.put(SecurityConstants.CALLBACK_HANDLER, "org.apache.cxf.systest.sts.common.CommonCallbackHandler"); properties.put("security.sts.token.username", "myclientkey"); properties.put("security.sts.token.properties", "clientKeystore.properties"); properties.put("security.sts.token.usecert", "true"); stsClient.setProperties(properties); ((BindingProvider)transportClaimsPort).getRequestContext().put(SecurityConstants.STS_CLIENT, stsClient); // Successful test // Add custom content to the RST stsClient.setCustomContent("<realm xmlns=\"http://cxf.apache.org/custom\">custom-realm</realm>"); doubleIt(transportClaimsPort, 25); ((java.io.Closeable)transportClaimsPort).close(); bus.shutdown(true); } // Here the custom parameter in the RST is parsed by the CustomUTValidator @org.junit.Test public void testCustomParameterInRST2Validator() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = CustomParameterTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); URL wsdl = CustomParameterTest.class.getResource("DoubleIt.wsdl"); Service service = Service.create(wsdl, SERVICE_QNAME); QName portQName = new QName(NAMESPACE, "DoubleItTransportCustomParameterPort"); DoubleItPortType transportClaimsPort = service.getPort(portQName, DoubleItPortType.class); updateAddressPort(transportClaimsPort, PORT); TokenTestUtils.updateSTSPort((BindingProvider)transportClaimsPort, STSPORT); STSClient stsClient = new STSClient(bus); stsClient.setWsdlLocation("https://localhost:" + STSPORT + "/SecurityTokenService/UT?wsdl"); stsClient.setServiceName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}SecurityTokenService"); stsClient.setEndpointName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}UT_Port"); Map<String, Object> properties = new HashMap<>(); properties.put(SecurityConstants.USERNAME, "alice"); properties.put(SecurityConstants.CALLBACK_HANDLER, "org.apache.cxf.systest.sts.common.CommonCallbackHandler"); properties.put("security.sts.token.username", "myclientkey"); properties.put("security.sts.token.properties", "clientKeystore.properties"); properties.put("security.sts.token.usecert", "true"); stsClient.setProperties(properties); ((BindingProvider)transportClaimsPort).getRequestContext().put(SecurityConstants.STS_CLIENT, stsClient); // Failing test // Add custom content to the RST stsClient.setCustomContent("<realm xmlns=\"http://cxf.apache.org/custom\">custom-unknown-realm</realm>"); try { doubleIt(transportClaimsPort, 25); fail("Failure expected on the wrong realm"); } catch (Exception ex) { // expected } ((java.io.Closeable)transportClaimsPort).close(); bus.shutdown(true); } // Here the custom parameter in the RST is parsed by the CustomClaimsHandler @org.junit.Test public void testCustomParameterInRSTClaimsHandler() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = CustomParameterTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); URL wsdl = CustomParameterTest.class.getResource("DoubleIt.wsdl"); Service service = Service.create(wsdl, SERVICE_QNAME); QName portQName = new QName(NAMESPACE, "DoubleItTransportCustomParameterClaimsPort"); DoubleItPortType transportClaimsPort = service.getPort(portQName, DoubleItPortType.class); updateAddressPort(transportClaimsPort, PORT); TokenTestUtils.updateSTSPort((BindingProvider)transportClaimsPort, STSPORT); STSClient stsClient = new STSClient(bus); stsClient.setWsdlLocation("https://localhost:" + STSPORT + "/SecurityTokenService/Transport?wsdl"); stsClient.setServiceName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}SecurityTokenService"); stsClient.setEndpointName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}Transport_Port"); Map<String, Object> properties = new HashMap<>(); properties.put(SecurityConstants.USERNAME, "alice"); properties.put(SecurityConstants.CALLBACK_HANDLER, "org.apache.cxf.systest.sts.common.CommonCallbackHandler"); properties.put("security.sts.token.username", "myclientkey"); properties.put("security.sts.token.properties", "clientKeystore.properties"); properties.put("security.sts.token.usecert", "true"); stsClient.setProperties(properties); ((BindingProvider)transportClaimsPort).getRequestContext().put(SecurityConstants.STS_CLIENT, stsClient); // Successful test // Add custom content to the RST stsClient.setCustomContent("<realm xmlns=\"http://cxf.apache.org/custom\">custom-realm</realm>"); doubleIt(transportClaimsPort, 25); ((java.io.Closeable)transportClaimsPort).close(); bus.shutdown(true); } // Here the custom parameter in the RST is parsed by the CustomClaimsHandler @org.junit.Test public void testCustomParameterInRSTClaimsHandler2() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = CustomParameterTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); URL wsdl = CustomParameterTest.class.getResource("DoubleIt.wsdl"); Service service = Service.create(wsdl, SERVICE_QNAME); QName portQName = new QName(NAMESPACE, "DoubleItTransportCustomParameterClaimsPort"); DoubleItPortType transportClaimsPort = service.getPort(portQName, DoubleItPortType.class); updateAddressPort(transportClaimsPort, PORT); TokenTestUtils.updateSTSPort((BindingProvider)transportClaimsPort, STSPORT); STSClient stsClient = new STSClient(bus); stsClient.setWsdlLocation("https://localhost:" + STSPORT + "/SecurityTokenService/Transport?wsdl"); stsClient.setServiceName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}SecurityTokenService"); stsClient.setEndpointName("{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}Transport_Port"); Map<String, Object> properties = new HashMap<>(); properties.put(SecurityConstants.USERNAME, "alice"); properties.put(SecurityConstants.CALLBACK_HANDLER, "org.apache.cxf.systest.sts.common.CommonCallbackHandler"); properties.put("security.sts.token.username", "myclientkey"); properties.put("security.sts.token.properties", "clientKeystore.properties"); properties.put("security.sts.token.usecert", "true"); stsClient.setProperties(properties); ((BindingProvider)transportClaimsPort).getRequestContext().put(SecurityConstants.STS_CLIENT, stsClient); // Failing test // Add custom content to the RST stsClient.setCustomContent("<realm xmlns=\"http://cxf.apache.org/custom\">custom-unknown-realm</realm>"); try { doubleIt(transportClaimsPort, 25); fail("Failure expected on the wrong realm"); } catch (Exception ex) { // expected } ((java.io.Closeable)transportClaimsPort).close(); bus.shutdown(true); } @org.junit.Test public void testCustomParameterToRESTInterface() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = CustomParameterTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); String address = "https://localhost:" + STSPORT + "/SecurityTokenServiceREST/token"; WebClient client = WebClient.create(address, busFile.toString()); client.type("application/xml").accept("application/xml"); // Create RequestSecurityToken W3CDOMStreamWriter writer = new W3CDOMStreamWriter(); String namespace = STSUtils.WST_NS_05_12; writer.writeStartElement("wst", "RequestSecurityToken", namespace); writer.writeNamespace("wst", namespace); writer.writeStartElement("wst", "RequestType", namespace); writer.writeCharacters(namespace + "/Issue"); writer.writeEndElement(); writer.writeStartElement("wst", "TokenType", namespace); writer.writeCharacters(SAML2_TOKEN_TYPE); writer.writeEndElement(); writer.writeStartElement("wst", "Claims", namespace); writer.writeAttribute("Dialect", "http://schemas.xmlsoap.org/ws/2005/05/identity"); writer.writeStartElement("ic", "ClaimType", "http://schemas.xmlsoap.org/ws/2005/05/identity"); writer.writeAttribute("Uri", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"); writer.writeEndElement(); writer.writeEndElement(); // Add custom content to the RST writer.writeStartElement("", "realm", "http://cxf.apache.org/custom"); writer.writeCharacters("custom-realm"); writer.writeEndElement(); writer.writeEndElement(); Response response = client.post(new DOMSource(writer.getDocument().getDocumentElement())); RequestSecurityTokenResponseType securityResponse = response.readEntity(RequestSecurityTokenResponseType.class); Element assertion = validateSAMLSecurityTokenResponse(securityResponse, true); assertTrue(DOM2Writer.nodeToString(assertion).contains("admin-user")); bus.shutdown(true); } private Element validateSAMLSecurityTokenResponse( RequestSecurityTokenResponseType securityResponse, boolean saml2 ) throws Exception { RequestedSecurityTokenType requestedSecurityToken = getRequestedSecurityToken(securityResponse); assertNotNull(requestedSecurityToken); // Process the token List<WSSecurityEngineResult> results = processToken((Element)requestedSecurityToken.getAny()); assertTrue(results != null && results.size() == 1); SamlAssertionWrapper assertion = (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION); assertTrue(assertion != null); if (saml2) { assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null); } else { assertTrue(assertion.getSaml2() == null && assertion.getSaml1() != null); } assertTrue(assertion.isSigned()); return (Element)results.get(0).get(WSSecurityEngineResult.TAG_TOKEN_ELEMENT); } private RequestedSecurityTokenType getRequestedSecurityToken(RequestSecurityTokenResponseType securityResponse) { for (Object obj : securityResponse.getAny()) { if (obj instanceof JAXBElement<?>) { JAXBElement<?> jaxbElement = (JAXBElement<?>)obj; if ("RequestedSecurityToken".equals(jaxbElement.getName().getLocalPart())) { return (RequestedSecurityTokenType)jaxbElement.getValue(); } } } return null; } private List<WSSecurityEngineResult> processToken(Element assertionElement) throws Exception { RequestData requestData = new RequestData(); requestData.setDisableBSPEnforcement(true); CallbackHandler callbackHandler = new org.apache.cxf.systest.sts.common.CommonCallbackHandler(); requestData.setCallbackHandler(callbackHandler); Crypto crypto = CryptoFactory.getInstance("serviceKeystore.properties"); requestData.setDecCrypto(crypto); requestData.setSigVerCrypto(crypto); requestData.setWsDocInfo(new WSDocInfo(assertionElement.getOwnerDocument())); Processor processor = new SAMLTokenProcessor(); return processor.handleToken(assertionElement, requestData); } private static void doubleIt(DoubleItPortType port, int numToDouble) { int resp = port.doubleIt(numToDouble); assertEquals(numToDouble * 2, resp); } }