[CXF-6272] - SCT Renew in Secure Conversation. Thanks to Freddy Exposito for the patch. - Also added a unit test. - Also explicitly removed the token to be renewed from the cache first
Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/53c9848b Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/53c9848b Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/53c9848b Branch: refs/heads/3.0.x-fixes Commit: 53c9848bfcd464f2e2db5449d8f1d1d1ce5a7991 Parents: e57a012 Author: Colm O hEigeartaigh <[email protected]> Authored: Fri Feb 27 14:25:03 2015 +0000 Committer: Colm O hEigeartaigh <[email protected]> Committed: Fri Feb 27 15:04:50 2015 +0000 ---------------------------------------------------------------------- .../policy/interceptors/STSInvoker.java | 28 ++++--- .../SecureConversationInInterceptor.java | 77 ++++++++++++++------ .../SpnegoContextTokenInInterceptor.java | 14 +++- .../apache/cxf/ws/security/trust/STSUtils.java | 43 ++++++++++- .../cxf/systest/ws/wssc/WSSCUnitTest.java | 35 +++++++++ 5 files changed, 162 insertions(+), 35 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/53c9848b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/STSInvoker.java ---------------------------------------------------------------------- diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/STSInvoker.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/STSInvoker.java index e2ea19a..a4ecd86 100644 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/STSInvoker.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/STSInvoker.java @@ -84,7 +84,7 @@ abstract class STSInvoker implements Invoker { } String namespace = requestEl.getNamespaceURI(); String prefix = requestEl.getPrefix(); - SecurityToken cancelToken = null; + SecurityToken cancelOrRenewToken = null; if ("RequestSecurityToken".equals(requestEl.getLocalName())) { try { String requestType = null; @@ -96,8 +96,8 @@ abstract class STSInvoker implements Invoker { if (namespace.equals(el.getNamespaceURI())) { if ("RequestType".equals(localName)) { requestType = el.getTextContent(); - } else if ("CancelTarget".equals(localName)) { - cancelToken = findCancelToken(exchange, el); + } else if ("CancelTarget".equals(localName) || "RenewTarget".equals(localName)) { + cancelOrRenewToken = findCancelOrRenewToken(exchange, el); } else if ("BinaryExchange".equals(localName)) { binaryExchange = el; } else if ("TokenType".equals(localName)) { @@ -121,10 +121,10 @@ abstract class STSInvoker implements Invoker { if (requestType.endsWith("/Issue")) { doIssue(requestEl, exchange, binaryExchange, writer, prefix, namespace); } else if (requestType.endsWith("/Cancel")) { - doCancel(exchange, cancelToken, writer, prefix, namespace); - } //else if (requestType.endsWith("/Renew")) { - //REVISIT - implement - //} + doCancel(exchange, cancelOrRenewToken, writer, prefix, namespace); + } else if (requestType.endsWith("/Renew")) { + doRenew(requestEl, exchange, cancelOrRenewToken, binaryExchange, writer, prefix, namespace); + } return new MessageContentsList(new DOMSource(writer.getDocument())); } catch (RuntimeException ex) { @@ -146,9 +146,19 @@ abstract class STSInvoker implements Invoker { String namespace ) throws Exception; + abstract void doRenew( + Element requestEl, + Exchange exchange, + SecurityToken renewToken, + Element binaryExchange, + W3CDOMStreamWriter writer, + String prefix, + String namespace + ) throws Exception; + private void doCancel( Exchange exchange, - SecurityToken cancelToken, + SecurityToken cancelToken, W3CDOMStreamWriter writer, String prefix, String namespace @@ -171,7 +181,7 @@ abstract class STSInvoker implements Invoker { } } - private SecurityToken findCancelToken(Exchange exchange, Element el) throws WSSecurityException { + private SecurityToken findCancelOrRenewToken(Exchange exchange, Element el) throws WSSecurityException { Element childElement = DOMUtils.getFirstElement(el); String uri = ""; if ("SecurityContextToken".equals(childElement.getLocalName())) { http://git-wip-us.apache.org/repos/asf/cxf/blob/53c9848b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/SecureConversationInInterceptor.java ---------------------------------------------------------------------- diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/SecureConversationInInterceptor.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/SecureConversationInInterceptor.java index 6cb52d1..ef97425 100644 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/SecureConversationInInterceptor.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/SecureConversationInInterceptor.java @@ -62,6 +62,7 @@ import org.apache.neethi.All; import org.apache.neethi.Assertion; import org.apache.neethi.ExactlyOne; import org.apache.neethi.Policy; +import org.apache.wss4j.dom.WSSConfig; import org.apache.wss4j.dom.message.token.SecurityContextToken; import org.apache.wss4j.policy.SP12Constants; import org.apache.wss4j.policy.SPConstants; @@ -172,8 +173,8 @@ class SecureConversationInInterceptor extends AbstractPhaseInterceptor<SoapMessa SecureConversationToken tok = (SecureConversationToken)ais.iterator() .next().getAssertion(); Policy pol = tok.getBootstrapPolicy().getPolicy(); - if (s.endsWith("Cancel") || s.endsWith("/Renew")) { - //Cancel and Renew just sign with the token + if (s.endsWith("Cancel")) { + //Cancel just sign with the token Policy p = new Policy(); ExactlyOne ea = new ExactlyOne(); p.addPolicyComponent(ea); @@ -314,11 +315,34 @@ class SecureConversationInInterceptor extends AbstractPhaseInterceptor<SoapMessa String prefix, String namespace ) throws Exception { + doIssueOrRenew(requestEl, exchange, binaryExchange, writer, prefix, namespace, null); + } + + void doRenew(Element requestEl, + Exchange exchange, + SecurityToken renewToken, + Element binaryExchange, + W3CDOMStreamWriter writer, + String prefix, + String namespace) throws Exception { + doIssueOrRenew(requestEl, exchange, binaryExchange, writer, prefix, namespace, + renewToken.getId()); + } + + + private void doIssueOrRenew(Element requestEl, + Exchange exchange, + Element binaryExchange, + W3CDOMStreamWriter writer, + String prefix, + String namespace, + String tokenIdToRenew + ) throws Exception { if (STSUtils.WST_NS_05_12.equals(namespace)) { writer.writeStartElement(prefix, "RequestSecurityTokenResponseCollection", namespace); } writer.writeStartElement(prefix, "RequestSecurityTokenResponse", namespace); - + byte clientEntropy[] = null; int keySize = 256; long ttl = 300000L; @@ -338,53 +362,64 @@ class SecureConversationInInterceptor extends AbstractPhaseInterceptor<SoapMessa tokenType = el.getTextContent(); } } - + el = DOMUtils.getNextElement(el); } - + // Check received KeySize if (keySize < 128 || keySize > 512) { keySize = 256; } - + writer.writeStartElement(prefix, "RequestedSecurityToken", namespace); - SecurityContextToken sct = - new SecurityContextToken(NegotiationUtils.getWSCVersion(tokenType), writer.getDocument()); - + SecurityContextToken sct; + if (tokenIdToRenew != null) { + ((TokenStore)exchange.get(Endpoint.class).getEndpointInfo() + .getProperty(TokenStore.class.getName())).remove(tokenIdToRenew); + sct = new SecurityContextToken( + NegotiationUtils.getWSCVersion(tokenType), writer.getDocument(), + tokenIdToRenew); + sct.setID(WSSConfig.getNewInstance().getIdAllocator() + .createSecureId("sctId-", sct.getElement())); + } else { + sct = new SecurityContextToken( + NegotiationUtils.getWSCVersion(tokenType), writer.getDocument()); + } + Date created = new Date(); Date expires = new Date(); expires.setTime(created.getTime() + ttl); - + SecurityToken token = new SecurityToken(sct.getIdentifier(), created, expires); token.setToken(sct.getElement()); token.setTokenType(sct.getTokenType()); - + writer.getCurrentNode().appendChild(sct.getElement()); - writer.writeEndElement(); - + writer.writeEndElement(); + writer.writeStartElement(prefix, "RequestedAttachedReference", namespace); token.setAttachedReference( writeSecurityTokenReference(writer, "#" + sct.getID(), tokenType) ); writer.writeEndElement(); - + writer.writeStartElement(prefix, "RequestedUnattachedReference", namespace); token.setUnattachedReference( writeSecurityTokenReference(writer, sct.getIdentifier(), tokenType) ); writer.writeEndElement(); - + writeLifetime(writer, created, expires, prefix, namespace); byte[] secret = writeProofToken(prefix, namespace, writer, clientEntropy, keySize); - + token.setSecret(secret); - + SecurityContext sc = exchange.getInMessage().get(SecurityContext.class); if (sc != null) { token.setSecurityContext(sc); } - + // Get Bootstrap Token SecurityToken bootstrapToken = getBootstrapToken(exchange.getInMessage()); if (bootstrapToken != null) { @@ -392,11 +427,11 @@ class SecureConversationInInterceptor extends AbstractPhaseInterceptor<SoapMessa properties.put(SecurityToken.BOOTSTRAP_TOKEN_ID, bootstrapToken.getId()); token.setProperties(properties); } - + ((TokenStore)exchange.get(Endpoint.class).getEndpointInfo() .getProperty(TokenStore.class.getName())).add(token); - - + + writer.writeEndElement(); if (STSUtils.WST_NS_05_12.equals(namespace)) { writer.writeEndElement(); http://git-wip-us.apache.org/repos/asf/cxf/blob/53c9848b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/SpnegoContextTokenInInterceptor.java ---------------------------------------------------------------------- diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/SpnegoContextTokenInInterceptor.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/SpnegoContextTokenInInterceptor.java index 6b76879..c2e9d83 100644 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/SpnegoContextTokenInInterceptor.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/policy/interceptors/SpnegoContextTokenInInterceptor.java @@ -251,7 +251,19 @@ class SpnegoContextTokenInInterceptor extends AbstractPhaseInterceptor<SoapMessa ((TokenStore)exchange.get(Endpoint.class).getEndpointInfo() .getProperty(TokenStore.class.getName())).add(token); } - + + void doRenew( + Element requestEl, + Exchange exchange, + SecurityToken securityToken, + Element binaryExchange, + W3CDOMStreamWriter writer, + String prefix, + String namespace + ) { + //Not implemented + } + private SpnegoTokenContext handleBinaryExchange( Element binaryExchange, Message message, http://git-wip-us.apache.org/repos/asf/cxf/blob/53c9848b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSUtils.java ---------------------------------------------------------------------- diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSUtils.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSUtils.java index 20a4434..bc4d830 100644 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSUtils.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/trust/STSUtils.java @@ -244,14 +244,15 @@ public final class STSUtils { OperationInfo ioi = addIssueOperation(ii, namespace, ns); OperationInfo coi = addCancelOperation(ii, namespace, ns); - + OperationInfo roi = addRenewOperation(ii, namespace, ns); + si.setInterface(ii); service = new ServiceImpl(si); BindingFactoryManager bfm = bus.getExtension(BindingFactoryManager.class); BindingFactory bindingFactory = bfm.getBindingFactory(soapVersion); - BindingInfo bi = bindingFactory.createBindingInfo(service, - soapVersion, null); + BindingInfo bi = bindingFactory.createBindingInfo(service, + soapVersion, null); si.addBinding(bi); if (transportId == null) { ConduitInitiatorManager cim = bus.getExtension(ConduitInitiatorManager.class); @@ -274,7 +275,7 @@ public final class STSUtils { boi.addExtensor(soi); } soi.setAction(namespace + (sc ? "/RST/SCT" : "/RST/Issue")); - + boi = bi.getOperation(coi); soi = boi.getExtensor(SoapOperationInfo.class); if (soi == null) { @@ -282,6 +283,15 @@ public final class STSUtils { boi.addExtensor(soi); } soi.setAction(namespace + (sc ? "/RST/SCT/Cancel" : "/RST/Cancel")); + + boi = bi.getOperation(roi); + soi = boi.getExtensor(SoapOperationInfo.class); + if (soi == null) { + soi = new SoapOperationInfo(); + boi.addExtensor(soi); + } + soi.setAction(namespace + (sc ? "/RST/SCT/Renew" : "/RST/Renew")); + service.setDataBinding(new SourceDataBinding()); return new EndpointImpl(bus, service, ei); } @@ -332,4 +342,29 @@ public final class STSUtils { } return oi; } + + private static OperationInfo addRenewOperation(InterfaceInfo ii, + String namespace, + String servNamespace) { + OperationInfo oi = ii.addOperation(new QName(servNamespace, "RenewSecurityToken")); + MessageInfo mii = oi.createMessage(new QName(servNamespace, "RenewSecurityTokenMsg"), + MessageInfo.Type.INPUT); + oi.setInput("RenewSecurityTokenMsg", mii); + MessagePartInfo mpi = mii.addMessagePart("request"); + mpi.setElementQName(new QName(namespace, "RequestSecurityToken")); + + MessageInfo mio = oi.createMessage(new QName(servNamespace, + "RenewSecurityTokenResponseMsg"), + MessageInfo.Type.OUTPUT); + oi.setOutput("RenewSecurityTokenResponseMsg", mio); + mpi = mio.addMessagePart("response"); + + if (WST_NS_05_02.equals(namespace)) { + mpi.setElementQName(new QName(namespace, "RequestSecurityTokenResponse")); + } else { + mpi.setElementQName(new QName(namespace, "RequestSecurityTokenResponseCollection")); + } + return oi; + } + } http://git-wip-us.apache.org/repos/asf/cxf/blob/53c9848b/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/wssc/WSSCUnitTest.java ---------------------------------------------------------------------- diff --git a/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/wssc/WSSCUnitTest.java b/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/wssc/WSSCUnitTest.java index 9bb608e..675d2c0 100644 --- a/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/wssc/WSSCUnitTest.java +++ b/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/wssc/WSSCUnitTest.java @@ -220,6 +220,41 @@ public class WSSCUnitTest extends AbstractBusClientServerTestBase { assertTrue(stsClient.cancelSecurityToken(securityToken)); } + + @Test + public void testIssueAndRenewUnitTest() throws Exception { + if (test.isStreaming()) { + return; + } + + SpringBusFactory bf = new SpringBusFactory(); + URL busFile = WSSCUnitTest.class.getResource("client.xml"); + + Bus bus = bf.createBus(busFile.toString()); + SpringBusFactory.setDefaultBus(bus); + SpringBusFactory.setThreadDefaultBus(bus); + + STSClient stsClient = new STSClient(bus); + stsClient.setSecureConv(true); + stsClient.setLocation("http://localhost:" + PORT2 + "/" + "DoubleItSymmetric"); + + stsClient.setPolicy(createSymmetricBindingPolicy()); + + Map<String, Object> properties = new HashMap<String, Object>(); + properties.put("ws-security.encryption.username", "bob"); + TokenCallbackHandler callbackHandler = new TokenCallbackHandler(); + properties.put("ws-security.callback-handler", callbackHandler); + properties.put("ws-security.signature.properties", "alice.properties"); + properties.put("ws-security.encryption.properties", "bob.properties"); + stsClient.setProperties(properties); + + SecurityToken securityToken = + stsClient.requestSecurityToken("http://localhost:" + PORT2 + "/" + "DoubleItSymmetric"); + assertNotNull(securityToken); + callbackHandler.setSecurityToken(securityToken); + + assertNotNull(stsClient.renewSecurityToken(securityToken)); + } // mock up a SymmetricBinding policy to talk to the STS private Policy createSymmetricBindingPolicy() {
