Skip to content

Commit b84dca7

Browse files
committed
SWS-285
1 parent 1fc1b9b commit b84dca7

16 files changed

+205
-22
lines changed

security/src/main/java/org/springframework/ws/soap/security/AbstractWsSecurityInterceptor.java

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -127,27 +127,33 @@ public final boolean handleRequest(MessageContext messageContext, Object endpoin
127127
* @see #secureMessage(org.springframework.ws.soap.SoapMessage,org.springframework.ws.context.MessageContext)
128128
*/
129129
public final boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
130-
if (secureResponse) {
131-
Assert.isTrue(messageContext.hasResponse(), "MessageContext contains no response");
132-
Assert.isInstanceOf(SoapMessage.class, messageContext.getResponse());
133-
try {
134-
secureMessage((SoapMessage) messageContext.getResponse(), messageContext);
135-
return true;
136-
}
137-
catch (WsSecuritySecurementException ex) {
138-
return handleSecurementException(ex, messageContext);
130+
try {
131+
if (secureResponse) {
132+
Assert.isTrue(messageContext.hasResponse(), "MessageContext contains no response");
133+
Assert.isInstanceOf(SoapMessage.class, messageContext.getResponse());
134+
try {
135+
secureMessage((SoapMessage) messageContext.getResponse(), messageContext);
136+
return true;
137+
}
138+
catch (WsSecuritySecurementException ex) {
139+
return handleSecurementException(ex, messageContext);
140+
}
141+
catch (WsSecurityFaultException ex) {
142+
return handleFaultException(ex, messageContext);
143+
}
139144
}
140-
catch (WsSecurityFaultException ex) {
141-
return handleFaultException(ex, messageContext);
145+
else {
146+
return true;
142147
}
143148
}
144-
else {
145-
return true;
149+
finally {
150+
cleanUp();
146151
}
147152
}
148153

149154
/** Returns <code>true</code>, i.e. fault responses are not secured. */
150155
public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception {
156+
cleanUp();
151157
return true;
152158
}
153159

@@ -296,4 +302,6 @@ protected abstract void validateMessage(SoapMessage soapMessage, MessageContext
296302
*/
297303
protected abstract void secureMessage(SoapMessage soapMessage, MessageContext messageContext)
298304
throws WsSecuritySecurementException;
305+
306+
protected abstract void cleanUp();
299307
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2008 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ws.soap.security.callback;
18+
19+
import java.io.Serializable;
20+
import javax.security.auth.callback.Callback;
21+
22+
/**
23+
* Underlying security services instantiate and pass a <code>CleanupCallback</code> to the <code>handle</code> method of
24+
* a <code>CallbackHandler</code> to clean up security state.
25+
*
26+
* @author Arjen Poutsma
27+
* @since 1.0.4
28+
*/
29+
public class CleanupCallback implements Callback, Serializable {
30+
31+
private static final long serialVersionUID = 4744181820980888237L;
32+
33+
}

security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616

1717
package org.springframework.ws.soap.security.wss4j;
1818

19+
import java.io.IOException;
1920
import java.security.cert.X509Certificate;
2021
import java.util.Vector;
22+
import javax.security.auth.callback.Callback;
2123
import javax.security.auth.callback.CallbackHandler;
24+
import javax.security.auth.callback.UnsupportedCallbackException;
2225

2326
import org.apache.axiom.soap.SOAPEnvelope;
2427
import org.apache.axiom.soap.SOAPFactory;
@@ -47,6 +50,7 @@
4750
import org.springframework.ws.soap.security.WsSecuritySecurementException;
4851
import org.springframework.ws.soap.security.WsSecurityValidationException;
4952
import org.springframework.ws.soap.security.callback.CallbackHandlerChain;
53+
import org.springframework.ws.soap.security.callback.CleanupCallback;
5054

5155
/**
5256
* A WS-Security endpoint interceptor based on Apache's WSS4J. This inteceptor supports messages created by the {@link
@@ -599,4 +603,18 @@ private void replaceMessage(SoapMessage soapMessage, Document envelope) {
599603
}
600604
}
601605

606+
protected void cleanUp() {
607+
if (validationCallbackHandler != null) {
608+
try {
609+
CleanupCallback cleanupCallback = new CleanupCallback();
610+
validationCallbackHandler.handle(new Callback[]{cleanupCallback});
611+
}
612+
catch (IOException ex) {
613+
logger.warn("Cleanup callback resulted in IOException", ex);
614+
}
615+
catch (UnsupportedCallbackException ex) {
616+
// ignore
617+
}
618+
}
619+
}
602620
}

security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/AbstractWsPasswordCallbackHandler.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.apache.ws.security.WSPasswordCallback;
2424

2525
import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
26+
import org.springframework.ws.soap.security.callback.CleanupCallback;
2627

2728
/**
2829
* Abstract base class for {@link javax.security.auth.callback.CallbackHandler} implementations that handle {@link
@@ -74,6 +75,10 @@ protected final void handleInternal(Callback callback) throws IOException, Unsup
7475
"Unknown usage [" + passwordCallback.getUsage() + "]");
7576
}
7677
}
78+
else if (callback instanceof CleanupCallback) {
79+
CleanupCallback cleanupCallback = (CleanupCallback) callback;
80+
handleCleanup(cleanupCallback);
81+
}
7782
else {
7883
throw new UnsupportedCallbackException(callback);
7984
}
@@ -173,4 +178,13 @@ protected void handleCustomToken(WSPasswordCallback callback) throws IOException
173178
protected void handleEncryptedKeyToken(Callback callback) throws IOException, UnsupportedCallbackException {
174179
throw new UnsupportedCallbackException(callback);
175180
}
181+
182+
/**
183+
* Invoked when a {@link CleanupCallback} is passed to {@link #handle(Callback[])}.
184+
* <p/>
185+
* Default implementation throws an {@link UnsupportedCallbackException}.
186+
*/
187+
protected void handleCleanup(CleanupCallback callback) throws IOException, UnsupportedCallbackException {
188+
throw new UnsupportedCallbackException(callback);
189+
}
176190
}

security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiDigestPasswordValidationCallbackHandler.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.IOException;
2020
import javax.security.auth.callback.UnsupportedCallbackException;
2121

22+
import org.acegisecurity.context.SecurityContextHolder;
2223
import org.acegisecurity.providers.dao.UserCache;
2324
import org.acegisecurity.providers.dao.cache.NullUserCache;
2425
import org.acegisecurity.userdetails.UserDetails;
@@ -28,6 +29,7 @@
2829

2930
import org.springframework.dao.DataAccessException;
3031
import org.springframework.util.Assert;
32+
import org.springframework.ws.soap.security.callback.CleanupCallback;
3133
import org.springframework.ws.soap.security.wss4j.callback.AbstractWsPasswordCallbackHandler;
3234

3335
/**
@@ -70,6 +72,10 @@ protected void handleUsernameToken(WSPasswordCallback callback) throws IOExcepti
7072
}
7173
}
7274

75+
protected void handleCleanup(CleanupCallback callback) throws IOException, UnsupportedCallbackException {
76+
SecurityContextHolder.clearContext();
77+
}
78+
7379
private UserDetails loadUserDetails(String username) throws DataAccessException {
7480
UserDetails user = userCache.getUserFromCache(username);
7581

security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/acegi/AcegiPlainTextPasswordValidationCallbackHandler.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.apache.ws.security.WSSecurityException;
2929

3030
import org.springframework.util.Assert;
31+
import org.springframework.ws.soap.security.callback.CleanupCallback;
3132
import org.springframework.ws.soap.security.wss4j.callback.AbstractWsPasswordCallbackHandler;
3233

3334
/**
@@ -62,6 +63,10 @@ public void afterPropertiesSet() throws Exception {
6263
Assert.notNull(authenticationManager, "authenticationManager is required");
6364
}
6465

66+
protected void handleCleanup(CleanupCallback callback) throws IOException, UnsupportedCallbackException {
67+
SecurityContextHolder.clearContext();
68+
}
69+
6570
protected void handleUsernameTokenUnknown(WSPasswordCallback callback)
6671
throws IOException, UnsupportedCallbackException {
6772
String identifier = callback.getIdentifer();
@@ -77,7 +82,7 @@ protected void handleUsernameTokenUnknown(WSPasswordCallback callback)
7782
if (logger.isDebugEnabled()) {
7883
logger.debug("Authentication request for user '" + identifier + "' failed: " + failed.toString());
7984
}
80-
SecurityContextHolder.getContext().setAuthentication(null);
85+
SecurityContextHolder.clearContext();
8186
if (!ignoreFailure) {
8287
throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
8388
}

security/src/main/java/org/springframework/ws/soap/security/xwss/XwsSecurityInterceptor.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616

1717
package org.springframework.ws.soap.security.xwss;
1818

19+
import java.io.IOException;
1920
import java.io.InputStream;
21+
import javax.security.auth.callback.Callback;
2022
import javax.security.auth.callback.CallbackHandler;
23+
import javax.security.auth.callback.UnsupportedCallbackException;
2124
import javax.xml.soap.SOAPMessage;
2225

2326
import com.sun.xml.wss.ProcessingContext;
@@ -34,6 +37,7 @@
3437
import org.springframework.ws.soap.saaj.SaajSoapMessage;
3538
import org.springframework.ws.soap.security.AbstractWsSecurityInterceptor;
3639
import org.springframework.ws.soap.security.WsSecurityValidationException;
40+
import org.springframework.ws.soap.security.callback.CleanupCallback;
3741
import org.springframework.ws.soap.security.xwss.callback.XwssCallbackHandlerChain;
3842

3943
/**
@@ -164,4 +168,18 @@ protected void validateMessage(SoapMessage soapMessage, MessageContext messageCo
164168
}
165169
}
166170

171+
protected void cleanUp() {
172+
if (callbackHandler != null) {
173+
try {
174+
CleanupCallback cleanupCallback = new CleanupCallback();
175+
callbackHandler.handle(new Callback[]{cleanupCallback});
176+
}
177+
catch (IOException ex) {
178+
logger.warn("Cleanup callback resulted in IOException", ex);
179+
}
180+
catch (UnsupportedCallbackException ex) {
181+
// ignore
182+
}
183+
}
184+
}
167185
}

security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiCertificateValidationCallbackHandler.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import org.springframework.util.Assert;
3232
import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
33+
import org.springframework.ws.soap.security.callback.CleanupCallback;
3334

3435
/**
3536
* Callback handler that validates a certificate using an Acegi <code>AuthenticationManager</code>. Logic based on
@@ -76,6 +77,9 @@ protected void handleInternal(Callback callback) throws IOException, Unsupported
7677
if (callback instanceof CertificateValidationCallback) {
7778
((CertificateValidationCallback) callback).setValidator(new AcegiCertificateValidator());
7879
}
80+
else if (callback instanceof CleanupCallback) {
81+
SecurityContextHolder.clearContext();
82+
}
7983
else {
8084
throw new UnsupportedCallbackException(callback);
8185
}
@@ -101,7 +105,7 @@ public boolean validate(X509Certificate certificate)
101105
logger.debug("Authentication request for certificate with DN [" +
102106
certificate.getSubjectX500Principal().getName() + "] failed: " + failed.toString());
103107
}
104-
SecurityContextHolder.getContext().setAuthentication(null);
108+
SecurityContextHolder.clearContext();
105109
result = ignoreFailure;
106110
}
107111
return result;

security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiDigestPasswordValidationCallbackHandler.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.dao.DataAccessException;
3434
import org.springframework.util.Assert;
3535
import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
36+
import org.springframework.ws.soap.security.callback.CleanupCallback;
3637
import org.springframework.ws.soap.security.xwss.callback.DefaultTimestampValidator;
3738

3839
/**
@@ -99,6 +100,10 @@ else if (callback instanceof TimestampValidationCallback) {
99100
timestampCallback.setValidator(new DefaultTimestampValidator());
100101

101102
}
103+
else if (callback instanceof CleanupCallback) {
104+
SecurityContextHolder.clearContext();
105+
return;
106+
}
102107
throw new UnsupportedCallbackException(callback);
103108
}
104109

security/src/main/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiPlainTextPasswordValidationCallbackHandler.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import org.springframework.util.Assert;
3131
import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
32+
import org.springframework.ws.soap.security.callback.CleanupCallback;
3233

3334
/**
3435
* Callback handler that validates a certificate uses an Acegi <code>AuthenticationManager</code>. Logic based on
@@ -81,6 +82,10 @@ protected void handleInternal(Callback callback) throws IOException, Unsupported
8182
return;
8283
}
8384
}
85+
else if (callback instanceof CleanupCallback) {
86+
SecurityContextHolder.clearContext();
87+
return;
88+
}
8489
throw new UnsupportedCallbackException(callback);
8590
}
8691

@@ -104,7 +109,7 @@ public boolean validate(PasswordValidationCallback.Request request)
104109
logger.debug("Authentication request for user '" + plainTextRequest.getUsername() + "' failed: " +
105110
failed.toString());
106111
}
107-
SecurityContextHolder.getContext().setAuthentication(null);
112+
SecurityContextHolder.clearContext();
108113
return ignoreFailure;
109114
}
110115
}

security/src/main/java/org/springframework/ws/soap/security/xwss/callback/jaas/JaasCertificateValidationCallbackHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ private class JaasCertificateValidator implements CertificateValidationCallback.
5858

5959
public boolean validate(X509Certificate certificate)
6060
throws CertificateValidationCallback.CertificateValidationException {
61-
LoginContext loginContext = null;
6261
Subject subject = new Subject();
6362
subject.getPrincipals().add(certificate.getSubjectX500Principal());
63+
LoginContext loginContext;
6464
try {
6565
loginContext = new LoginContext(getLoginContextName(), subject);
6666
}

security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorAcegiCallbackHandlerTestCase.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ protected void onSetup() throws Exception {
3535

3636
protected void tearDown() throws Exception {
3737
control.verify();
38+
SecurityContextHolder.clearContext();
3839
}
3940

4041
public void testValidateUsernameTokenPlainText() throws Exception {
@@ -43,6 +44,11 @@ public void testValidateUsernameTokenPlainText() throws Exception {
4344
MessageContext messageContext = new DefaultMessageContext(message, getMessageFactory());
4445
interceptor.handleRequest(messageContext, null);
4546
assertValidateUsernameToken(message);
47+
48+
// test clean up
49+
messageContext.getResponse();
50+
interceptor.handleResponse(messageContext, null);
51+
assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication());
4652
}
4753

4854
public void testValidateUsernameTokenDigest() throws Exception {
@@ -51,16 +57,19 @@ public void testValidateUsernameTokenDigest() throws Exception {
5157
MessageContext messageContext = new DefaultMessageContext(message, getMessageFactory());
5258
interceptor.handleRequest(messageContext, null);
5359
assertValidateUsernameToken(message);
60+
61+
// test clean up
62+
messageContext.getResponse();
63+
interceptor.handleResponse(messageContext, null);
64+
assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication());
5465
}
5566

5667
protected void assertValidateUsernameToken(SoapMessage message) throws Exception {
5768
Object result = getMessage(message);
5869
assertNotNull("No result returned", result);
5970
assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security",
6071
getDocument(message));
61-
Authentication authentication = SecurityContextHolder.getContext()
62-
.getAuthentication();
63-
assertNotNull("authentication must not be null", authentication);
72+
assertNotNull("No Authentication created", SecurityContextHolder.getContext().getAuthentication());
6473
}
6574

6675
protected EndpointInterceptor prepareInterceptor(String actions, boolean validating, boolean digest)

0 commit comments

Comments
 (0)