Skip to content

Commit 41457d2

Browse files
committed
Fixed SWS-285 in 1.0 branch
1 parent 320dee5 commit 41457d2

12 files changed

+154
-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
@@ -88,29 +88,35 @@ public final boolean handleRequest(MessageContext messageContext, Object endpoin
8888
}
8989

9090
public final boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
91-
if (secureResponse) {
92-
Assert.isTrue(messageContext.getResponse() instanceof SoapMessage,
93-
"WsSecurityInterceptor requires a SoapMessage response");
94-
try {
95-
secureMessage((SoapMessage) messageContext.getResponse());
96-
return true;
97-
}
98-
catch (WsSecuritySecurementException ex) {
99-
return handleSecurementException(ex, messageContext);
91+
try {
92+
if (secureResponse) {
93+
Assert.isTrue(messageContext.hasResponse(), "MessageContext contains no response");
94+
Assert.isInstanceOf(SoapMessage.class, messageContext.getResponse());
95+
try {
96+
secureMessage((SoapMessage) messageContext.getResponse());
97+
return true;
98+
}
99+
catch (WsSecuritySecurementException ex) {
100+
return handleSecurementException(ex, messageContext);
101+
}
102+
catch (WsSecurityFaultException ex) {
103+
return handleFaultException(ex, messageContext);
104+
}
100105
}
101-
catch (WsSecurityFaultException ex) {
102-
return handleFaultException(ex, messageContext);
106+
else {
107+
return true;
103108
}
104109
}
105-
else {
106-
return true;
110+
finally {
111+
cleanUp();
107112
}
108113
}
109114

110115
/**
111116
* Returns <code>true</code>, i.e. faults are not secured.
112117
*/
113118
public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception {
119+
cleanUp();
114120
return true;
115121
}
116122

@@ -191,4 +197,6 @@ protected boolean handleFaultException(WsSecurityFaultException ex, MessageConte
191197
* @throws WsSecuritySecurementException in case of securement errors
192198
*/
193199
protected abstract void secureMessage(SoapMessage soapMessage) throws WsSecuritySecurementException;
200+
201+
protected abstract void cleanUp();
194202
}

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;
@@ -33,6 +36,7 @@
3336
import org.springframework.ws.soap.security.AbstractWsSecurityInterceptor;
3437
import org.springframework.ws.soap.security.WsSecurityValidationException;
3538
import org.springframework.ws.soap.security.xwss.callback.CallbackHandlerChain;
39+
import org.springframework.ws.soap.security.xwss.callback.CleanupCallback;
3640

3741
/**
3842
* WS-Security endpoint interceptor that is based on Sun's XML and Web Services Security package (XWSS). This
@@ -162,4 +166,18 @@ protected void validateMessage(SoapMessage soapMessage) throws WsSecurityValidat
162166
}
163167
}
164168

169+
protected void cleanUp() {
170+
if (callbackHandler != null) {
171+
try {
172+
CleanupCallback cleanupCallback = new CleanupCallback();
173+
callbackHandler.handle(new Callback[]{cleanupCallback});
174+
}
175+
catch (IOException ex) {
176+
logger.warn("Cleanup callback resulted in IOException", ex);
177+
}
178+
catch (UnsupportedCallbackException ex) {
179+
// ignore
180+
}
181+
}
182+
}
165183
}
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.xwss.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/xwss/callback/acegi/AcegiCertificateValidationCallbackHandler.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.acegisecurity.providers.x509.X509AuthenticationToken;
3030
import org.springframework.util.Assert;
3131
import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
32+
import org.springframework.ws.soap.security.xwss.callback.CleanupCallback;
3233

3334
/**
3435
* Callback handler that validates a certificate using an Acegi <code>AuthenticationManager</code>. Logic based on
@@ -78,6 +79,9 @@ protected void handleInternal(Callback callback) throws IOException, Unsupported
7879
if (callback instanceof CertificateValidationCallback) {
7980
((CertificateValidationCallback) callback).setValidator(new AcegiCertificateValidator());
8081
}
82+
else if (callback instanceof CleanupCallback) {
83+
SecurityContextHolder.clearContext();
84+
}
8185
else {
8286
throw new UnsupportedCallbackException(callback);
8387
}
@@ -103,7 +107,7 @@ public boolean validate(X509Certificate certificate)
103107
logger.debug("Authentication request for certificate with DN [" +
104108
certificate.getSubjectX500Principal().getName() + "] failed: " + failed.toString());
105109
}
106-
SecurityContextHolder.getContext().setAuthentication(null);
110+
SecurityContextHolder.clearContext();
107111
result = ignoreFailure;
108112
}
109113
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
@@ -32,6 +32,7 @@
3232
import org.springframework.dao.DataAccessException;
3333
import org.springframework.util.Assert;
3434
import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
35+
import org.springframework.ws.soap.security.xwss.callback.CleanupCallback;
3536
import org.springframework.ws.soap.security.xwss.callback.DefaultTimestampValidator;
3637

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

100101
}
102+
else if (callback instanceof CleanupCallback) {
103+
SecurityContextHolder.clearContext();
104+
return;
105+
}
101106
throw new UnsupportedCallbackException(callback);
102107
}
103108

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
@@ -28,6 +28,7 @@
2828
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
2929
import org.springframework.util.Assert;
3030
import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
31+
import org.springframework.ws.soap.security.xwss.callback.CleanupCallback;
3132

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

@@ -103,7 +108,7 @@ public boolean validate(PasswordValidationCallback.Request request)
103108
logger.debug("Authentication request for user '" + plainTextRequest.getUsername() + "' failed: " +
104109
failed.toString());
105110
}
106-
SecurityContextHolder.getContext().setAuthentication(null);
111+
SecurityContextHolder.clearContext();
107112
return ignoreFailure;
108113
}
109114
}

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/xwss/XwsSecurityInterceptorTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,9 @@ protected void validateMessage(SoapMessage soapMessage) throws WsSecurityValidat
7474
SOAPMessage request = messageFactory.createMessage();
7575
MessageContext context =
7676
new DefaultMessageContext(new SaajSoapMessage(request), new SaajSoapMessageFactory(messageFactory));
77+
context.getResponse();
7778
interceptor.handleResponse(context, null);
7879
assertEquals("Invalid response", securedResponse, ((SaajSoapMessage) context.getResponse()).getSaajMessage());
7980
}
8081

81-
}
82+
}

security/src/test/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiCertificateValidationCallbackHandlerTest.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@
2525
import org.acegisecurity.AuthenticationManager;
2626
import org.acegisecurity.BadCredentialsException;
2727
import org.acegisecurity.GrantedAuthority;
28+
import org.acegisecurity.context.SecurityContextHolder;
2829
import org.acegisecurity.providers.TestingAuthenticationToken;
2930
import org.acegisecurity.providers.x509.X509AuthenticationToken;
3031
import org.easymock.MockControl;
3132

3233
import org.springframework.core.io.ClassPathResource;
34+
import org.springframework.ws.soap.security.xwss.callback.CleanupCallback;
3335

3436
public class AcegiCertificateValidationCallbackHandlerTest extends TestCase {
3537

@@ -63,6 +65,10 @@ protected void setUp() throws Exception {
6365
callback = new CertificateValidationCallback(certificate);
6466
}
6567

68+
protected void tearDown() throws Exception {
69+
SecurityContextHolder.clearContext();
70+
}
71+
6672
public void testValidateCertificateValid() throws Exception {
6773
mock.authenticate(new X509AuthenticationToken(certificate));
6874
control.setMatcher(MockControl.ALWAYS_MATCHER);
@@ -71,6 +77,7 @@ public void testValidateCertificateValid() throws Exception {
7177
callbackHandler.handleInternal(callback);
7278
boolean authenticated = callback.getResult();
7379
assertTrue("Not authenticated", authenticated);
80+
assertNotNull("No Authentication created", SecurityContextHolder.getContext().getAuthentication());
7481
control.verify();
7582
}
7683

@@ -82,7 +89,18 @@ public void testValidateCertificateInvalid() throws Exception {
8289
callbackHandler.handleInternal(callback);
8390
boolean authenticated = callback.getResult();
8491
assertFalse("Authenticated", authenticated);
92+
assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication());
8593
control.verify();
8694
}
8795

88-
}
96+
public void testCleanUp() throws Exception {
97+
TestingAuthenticationToken authentication =
98+
new TestingAuthenticationToken(new Object(), new Object(), new GrantedAuthority[0]);
99+
SecurityContextHolder.getContext().setAuthentication(authentication);
100+
101+
CleanupCallback cleanupCallback = new CleanupCallback();
102+
callbackHandler.handleInternal(cleanupCallback);
103+
assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication());
104+
}
105+
106+
}

security/src/test/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiDigestPasswordValidationCallbackHandlerTest.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@
1919
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
2020
import junit.framework.TestCase;
2121
import org.acegisecurity.GrantedAuthority;
22+
import org.acegisecurity.context.SecurityContextHolder;
23+
import org.acegisecurity.providers.TestingAuthenticationToken;
2224
import org.acegisecurity.userdetails.User;
2325
import org.acegisecurity.userdetails.UserDetailsService;
2426
import org.acegisecurity.userdetails.UsernameNotFoundException;
2527
import org.easymock.MockControl;
2628

29+
import org.springframework.ws.soap.security.xwss.callback.CleanupCallback;
30+
2731
public class AcegiDigestPasswordValidationCallbackHandlerTest extends TestCase {
2832

2933
private AcegiDigestPasswordValidationCallbackHandler callbackHandler;
@@ -53,12 +57,17 @@ protected void setUp() throws Exception {
5357
callback = new PasswordValidationCallback(request);
5458
}
5559

60+
protected void tearDown() throws Exception {
61+
SecurityContextHolder.clearContext();
62+
}
63+
5664
public void testAuthenticateUserDigestUserNotFound() throws Exception {
5765
control.expectAndThrow(mock.loadUserByUsername(username), new UsernameNotFoundException(username));
5866
control.replay();
5967
callbackHandler.handleInternal(callback);
6068
boolean authenticated = callback.getResult();
6169
assertFalse("Authenticated", authenticated);
70+
assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication());
6271
control.verify();
6372
}
6473

@@ -69,6 +78,7 @@ public void testAuthenticateUserDigestValid() throws Exception {
6978
callbackHandler.handleInternal(callback);
7079
boolean authenticated = callback.getResult();
7180
assertTrue("Not authenticated", authenticated);
81+
assertNotNull("No Authentication created", SecurityContextHolder.getContext().getAuthentication());
7282
control.verify();
7383
}
7484

@@ -79,6 +89,18 @@ public void testAuthenticateUserDigestValidInvalid() throws Exception {
7989
callbackHandler.handleInternal(callback);
8090
boolean authenticated = callback.getResult();
8191
assertFalse("Authenticated", authenticated);
92+
assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication());
8293
control.verify();
8394
}
84-
}
95+
96+
public void testCleanUp() throws Exception {
97+
TestingAuthenticationToken authentication =
98+
new TestingAuthenticationToken(new Object(), new Object(), new GrantedAuthority[0]);
99+
SecurityContextHolder.getContext().setAuthentication(authentication);
100+
101+
CleanupCallback cleanupCallback = new CleanupCallback();
102+
callbackHandler.handleInternal(cleanupCallback);
103+
assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication());
104+
}
105+
106+
}

security/src/test/java/org/springframework/ws/soap/security/xwss/callback/acegi/AcegiPlainTextPasswordValidationCallbackHandlerTest.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@
2222
import org.acegisecurity.AuthenticationManager;
2323
import org.acegisecurity.BadCredentialsException;
2424
import org.acegisecurity.GrantedAuthority;
25+
import org.acegisecurity.context.SecurityContextHolder;
2526
import org.acegisecurity.providers.TestingAuthenticationToken;
2627
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
2728
import org.easymock.MockControl;
2829

30+
import org.springframework.ws.soap.security.xwss.callback.CleanupCallback;
31+
2932
public class AcegiPlainTextPasswordValidationCallbackHandlerTest extends TestCase {
3033

3134
private AcegiPlainTextPasswordValidationCallbackHandler callbackHandler;
@@ -52,6 +55,10 @@ protected void setUp() throws Exception {
5255
callback = new PasswordValidationCallback(request);
5356
}
5457

58+
protected void tearDown() throws Exception {
59+
SecurityContextHolder.clearContext();
60+
}
61+
5562
public void testAuthenticateUserPlainTextValid() throws Exception {
5663
Authentication authResult = new TestingAuthenticationToken(username, password, new GrantedAuthority[0]);
5764
control.expectAndReturn(mock.authenticate(new UsernamePasswordAuthenticationToken(username, password)),
@@ -60,6 +67,7 @@ public void testAuthenticateUserPlainTextValid() throws Exception {
6067
callbackHandler.handleInternal(callback);
6168
boolean authenticated = callback.getResult();
6269
assertTrue("Not authenticated", authenticated);
70+
assertNotNull("No Authentication created", SecurityContextHolder.getContext().getAuthentication());
6371
control.verify();
6472
}
6573

@@ -70,7 +78,18 @@ public void testAuthenticateUserPlainTextInvalid() throws Exception {
7078
callbackHandler.handleInternal(callback);
7179
boolean authenticated = callback.getResult();
7280
assertFalse("Authenticated", authenticated);
81+
assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication());
7382
control.verify();
7483
}
7584

76-
}
85+
public void testCleanUp() throws Exception {
86+
TestingAuthenticationToken authentication =
87+
new TestingAuthenticationToken(new Object(), new Object(), new GrantedAuthority[0]);
88+
SecurityContextHolder.getContext().setAuthentication(authentication);
89+
90+
CleanupCallback cleanupCallback = new CleanupCallback();
91+
callbackHandler.handleInternal(cleanupCallback);
92+
assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication());
93+
}
94+
95+
}

0 commit comments

Comments
 (0)