diff --git a/security/pom.xml b/security/pom.xml index 6958cf4b..af6c2812 100644 --- a/security/pom.xml +++ b/security/pom.xml @@ -1,4 +1,5 @@ - + spring-ws-parent org.springframework.ws @@ -21,6 +22,12 @@ Spring External Dependencies Repository https://springframework.svn.sourceforge.net/svnroot/springframework/repos/repo-ext/ + + + spring-s3 + Springframework Maven SNAPSHOT Repository + http://s3.amazonaws.com/maven.springframework.org/milestone + wso2 WSO2 Repository @@ -136,6 +143,12 @@ org.acegisecurity acegi-security + + + org.springframework.security + spring-security-core + true + javax.mail @@ -149,4 +162,4 @@ test - \ No newline at end of file + diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/springsecurity/SpringSecurityDigestPasswordValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/springsecurity/SpringSecurityDigestPasswordValidationCallbackHandler.java new file mode 100644 index 00000000..5233b40e --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/springsecurity/SpringSecurityDigestPasswordValidationCallbackHandler.java @@ -0,0 +1,110 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed 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.springframework.ws.soap.security.wss4j.callback.springsecurity; + +import java.io.IOException; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.apache.ws.security.WSPasswordCallback; +import org.apache.ws.security.WSUsernameTokenPrincipal; + +import org.springframework.dao.DataAccessException; +import org.springframework.security.context.SecurityContextHolder; +import org.springframework.security.providers.UsernamePasswordAuthenticationToken; +import org.springframework.security.providers.dao.UserCache; +import org.springframework.security.providers.dao.cache.NullUserCache; +import org.springframework.security.userdetails.UserDetails; +import org.springframework.security.userdetails.UserDetailsService; +import org.springframework.security.userdetails.UsernameNotFoundException; +import org.springframework.util.Assert; +import org.springframework.ws.soap.security.callback.CleanupCallback; +import org.springframework.ws.soap.security.wss4j.callback.AbstractWsPasswordCallbackHandler; +import org.springframework.ws.soap.security.wss4j.callback.UsernameTokenPrincipalCallback; + +/** + * Callback handler that validates a password digest using an Spring Security UserDetailsService. Logic + * based on Spring Security's DigestProcessingFilter. + *

+ * An Spring Security UserDetailService is used to load UserDetails from. The digest of the + * password contained in this details object is then compared with the digest in the message. + * + * @author Arjen Poutsma + * @see org.springframework.security.userdetails.UserDetailsService + * @see org.springframework.security.ui.digestauth.DigestProcessingFilter + * @since 1.5.0 + */ +public class SpringSecurityDigestPasswordValidationCallbackHandler extends AbstractWsPasswordCallbackHandler { + + private UserCache userCache = new NullUserCache(); + + private UserDetailsService userDetailsService; + + /** Sets the users cache. Not required, but can benefit performance. */ + public void setUserCache(UserCache userCache) { + this.userCache = userCache; + } + + /** Sets the Spring Security user details service. Required. */ + public void setUserDetailsService(UserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + + public void afterPropertiesSet() throws Exception { + Assert.notNull(userDetailsService, "userDetailsService is required"); + } + + protected void handleUsernameToken(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException { + String identifier = callback.getIdentifer(); + UserDetails user = loadUserDetails(identifier); + if (user != null) { + callback.setPassword(user.getPassword()); + } + } + + protected void handleUsernameTokenPrincipal(UsernameTokenPrincipalCallback callback) + throws IOException, UnsupportedCallbackException { + WSUsernameTokenPrincipal principal = callback.getPrincipal(); + UsernamePasswordAuthenticationToken authRequest = + new UsernamePasswordAuthenticationToken(principal, principal.getPassword()); + if (logger.isDebugEnabled()) { + logger.debug("Authentication success: " + authRequest.toString()); + } + SecurityContextHolder.getContext().setAuthentication(authRequest); + } + + protected void handleCleanup(CleanupCallback callback) throws IOException, UnsupportedCallbackException { + SecurityContextHolder.clearContext(); + } + + private UserDetails loadUserDetails(String username) throws DataAccessException { + UserDetails user = userCache.getUserFromCache(username); + + if (user == null) { + try { + user = userDetailsService.loadUserByUsername(username); + } + catch (UsernameNotFoundException notFound) { + if (logger.isDebugEnabled()) { + logger.debug("Username '" + username + "' not found"); + } + return null; + } + userCache.putUserInCache(user); + } + return user; + } +} \ No newline at end of file diff --git a/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/springsecurity/SpringSecurityPlainTextPasswordValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/springsecurity/SpringSecurityPlainTextPasswordValidationCallbackHandler.java new file mode 100644 index 00000000..7ac91cb6 --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/wss4j/callback/springsecurity/SpringSecurityPlainTextPasswordValidationCallbackHandler.java @@ -0,0 +1,91 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed 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.springframework.ws.soap.security.wss4j.callback.springsecurity; + +import java.io.IOException; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.apache.ws.security.WSPasswordCallback; +import org.apache.ws.security.WSSecurityException; + +import org.springframework.security.Authentication; +import org.springframework.security.AuthenticationException; +import org.springframework.security.AuthenticationManager; +import org.springframework.security.context.SecurityContextHolder; +import org.springframework.security.providers.UsernamePasswordAuthenticationToken; +import org.springframework.util.Assert; +import org.springframework.ws.soap.security.callback.CleanupCallback; +import org.springframework.ws.soap.security.wss4j.callback.AbstractWsPasswordCallbackHandler; + +/** + * Callback handler that validates a certificate uses an Spring Security AuthenticationManager. Logic based + * on Spring Security's BasicProcessingFilter. + *

+ * This handler requires an Spring Security AuthenticationManager to operate. It can be set using the + * authenticationManager property. An Spring Security UsernamePasswordAuthenticationToken is + * created with the username as principal and password as credentials. + * + * @author Arjen Poutsma + * @see org.springframework.security.providers.UsernamePasswordAuthenticationToken + * @see org.springframework.security.ui.basicauth.BasicProcessingFilter + * @since 1.5.0 + */ +public class SpringSecurityPlainTextPasswordValidationCallbackHandler extends AbstractWsPasswordCallbackHandler { + + private AuthenticationManager authenticationManager; + + private boolean ignoreFailure = false; + + /** Sets the Spring Security authentication manager. Required. */ + public void setAuthenticationManager(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + public void setIgnoreFailure(boolean ignoreFailure) { + this.ignoreFailure = ignoreFailure; + } + + public void afterPropertiesSet() throws Exception { + Assert.notNull(authenticationManager, "authenticationManager is required"); + } + + protected void handleCleanup(CleanupCallback callback) throws IOException, UnsupportedCallbackException { + SecurityContextHolder.clearContext(); + } + + protected void handleUsernameTokenUnknown(WSPasswordCallback callback) + throws IOException, UnsupportedCallbackException { + String identifier = callback.getIdentifer(); + try { + Authentication authResult = authenticationManager + .authenticate(new UsernamePasswordAuthenticationToken(identifier, callback.getPassword())); + if (logger.isDebugEnabled()) { + logger.debug("Authentication success: " + authResult.toString()); + } + SecurityContextHolder.getContext().setAuthentication(authResult); + } + catch (AuthenticationException failed) { + if (logger.isDebugEnabled()) { + logger.debug("Authentication request for user '" + identifier + "' failed: " + failed.toString()); + } + SecurityContextHolder.clearContext(); + if (!ignoreFailure) { + throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION); + } + } + } +} \ No newline at end of file diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityCertificateValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityCertificateValidationCallbackHandler.java new file mode 100644 index 00000000..7482befe --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityCertificateValidationCallbackHandler.java @@ -0,0 +1,116 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed 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.springframework.ws.soap.security.xwss.callback.springsecurity; + +import java.io.IOException; +import java.security.cert.X509Certificate; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.UnsupportedCallbackException; + +import com.sun.xml.wss.impl.callback.CertificateValidationCallback; + +import org.springframework.security.Authentication; +import org.springframework.security.AuthenticationException; +import org.springframework.security.AuthenticationManager; +import org.springframework.security.context.SecurityContextHolder; +import org.springframework.security.providers.x509.X509AuthenticationToken; +import org.springframework.util.Assert; +import org.springframework.ws.soap.security.callback.AbstractCallbackHandler; +import org.springframework.ws.soap.security.callback.CleanupCallback; + +/** + * Callback handler that validates a certificate using an Spring Security AuthenticationManager. Logic + * based on Spring Security's X509ProcessingFilter.

Spring Security + * X509AuthenticationToken is created with the certificate as the credentials.

The configured + * authentication manager is expected to supply a provider which can handle this token (usually an instance of + * X509AuthenticationProvider).

+ *

+ * This class only handles CertificateValidationCallbacks, and throws an + * UnsupportedCallbackException for others. + * + * @author Arjen Poutsma + * @see org.springframework.security.providers.x509.X509AuthenticationToken + * @see org.springframework.security.providers.x509.X509AuthenticationProvider + * @see org.springframework.security.ui.x509.X509ProcessingFilter + * @see com.sun.xml.wss.impl.callback.CertificateValidationCallback + * @since 1.5.0 + */ +public class SpringSecurityCertificateValidationCallbackHandler extends AbstractCallbackHandler { + + private AuthenticationManager authenticationManager; + + private boolean ignoreFailure = false; + + /** Sets the Spring Security authentication manager. Required. */ + public void setAuthenticationManager(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + public void setIgnoreFailure(boolean ignoreFailure) { + this.ignoreFailure = ignoreFailure; + } + + public void afterPropertiesSet() throws Exception { + Assert.notNull(authenticationManager, "authenticationManager is required"); + } + + /** + * Handles CertificateValidationCallbacks, and throws an UnsupportedCallbackException for + * others + * + * @throws javax.security.auth.callback.UnsupportedCallbackException + * when the callback is not supported + */ + protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException { + if (callback instanceof CertificateValidationCallback) { + ((CertificateValidationCallback) callback).setValidator(new SpringSecurityCertificateValidator()); + } + else if (callback instanceof CleanupCallback) { + SecurityContextHolder.clearContext(); + } + else { + throw new UnsupportedCallbackException(callback); + } + } + + private class SpringSecurityCertificateValidator implements CertificateValidationCallback.CertificateValidator { + + public boolean validate(X509Certificate certificate) + throws CertificateValidationCallback.CertificateValidationException { + boolean result; + try { + Authentication authResult = + authenticationManager.authenticate(new X509AuthenticationToken(certificate)); + if (logger.isDebugEnabled()) { + logger.debug("Authentication request for certificate with DN [" + + certificate.getSubjectX500Principal().getName() + "] successful"); + } + SecurityContextHolder.getContext().setAuthentication(authResult); + return true; + } + catch (AuthenticationException failed) { + if (logger.isDebugEnabled()) { + logger.debug("Authentication request for certificate with DN [" + + certificate.getSubjectX500Principal().getName() + "] failed: " + failed.toString()); + } + SecurityContextHolder.clearContext(); + result = ignoreFailure; + } + return result; + } + } +} \ No newline at end of file diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityDigestPasswordValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityDigestPasswordValidationCallbackHandler.java new file mode 100644 index 00000000..49f927f2 --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityDigestPasswordValidationCallbackHandler.java @@ -0,0 +1,155 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed 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.springframework.ws.soap.security.xwss.callback.springsecurity; + +import java.io.IOException; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.UnsupportedCallbackException; + +import com.sun.xml.wss.impl.callback.PasswordValidationCallback; +import com.sun.xml.wss.impl.callback.TimestampValidationCallback; + +import org.springframework.dao.DataAccessException; +import org.springframework.security.context.SecurityContextHolder; +import org.springframework.security.providers.UsernamePasswordAuthenticationToken; +import org.springframework.security.providers.dao.UserCache; +import org.springframework.security.providers.dao.cache.NullUserCache; +import org.springframework.security.userdetails.UserDetails; +import org.springframework.security.userdetails.UserDetailsService; +import org.springframework.security.userdetails.UsernameNotFoundException; +import org.springframework.util.Assert; +import org.springframework.ws.soap.security.callback.AbstractCallbackHandler; +import org.springframework.ws.soap.security.callback.CleanupCallback; +import org.springframework.ws.soap.security.xwss.callback.DefaultTimestampValidator; + +/** + * Callback handler that validates a password digest using an Spring Security UserDetailsService. Logic + * based on Spring Security's DigestProcessingFilter. + *

+ * An Spring Security UserDetailService is used to load UserDetails from. The digest of the + * password contained in this details object is then compared with the digest in the message. + *

+ * This class only handles PasswordValidationCallbacks that contain a DigestPasswordRequest, + * and throws an UnsupportedCallbackException for others. + * + * @author Arjen Poutsma + * @see org.springframework.security.userdetails.UserDetailsService + * @see com.sun.xml.wss.impl.callback.PasswordValidationCallback + * @see com.sun.xml.wss.impl.callback.PasswordValidationCallback.DigestPasswordRequest + * @see org.springframework.security.ui.digestauth.DigestProcessingFilter + * @since 1.5.0 + */ +public class SpringSecurityDigestPasswordValidationCallbackHandler extends AbstractCallbackHandler { + + private UserCache userCache = new NullUserCache(); + + private UserDetailsService userDetailsService; + + /** Sets the users cache. Not required, but can benefit performance. */ + public void setUserCache(UserCache userCache) { + this.userCache = userCache; + } + + /** Sets the Spring Security user details service. Required. */ + public void setUserDetailsService(UserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + + public void afterPropertiesSet() throws Exception { + Assert.notNull(userDetailsService, "userDetailsService is required"); + } + + /** + * Handles PasswordValidationCallbacks that contain a DigestPasswordRequest, and throws an + * UnsupportedCallbackException for others + * + * @throws javax.security.auth.callback.UnsupportedCallbackException + * when the callback is not supported + */ + protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException { + if (callback instanceof PasswordValidationCallback) { + PasswordValidationCallback passwordCallback = (PasswordValidationCallback) callback; + if (passwordCallback.getRequest() instanceof PasswordValidationCallback.DigestPasswordRequest) { + PasswordValidationCallback.DigestPasswordRequest request = + (PasswordValidationCallback.DigestPasswordRequest) passwordCallback.getRequest(); + String username = request.getUsername(); + UserDetails user = loadUserDetails(username); + if (user != null) { + request.setPassword(user.getPassword()); + } + SpringSecurityDigestPasswordValidator validator = new SpringSecurityDigestPasswordValidator(user); + passwordCallback.setValidator(validator); + return; + } + } + else if (callback instanceof TimestampValidationCallback) { + TimestampValidationCallback timestampCallback = (TimestampValidationCallback) callback; + timestampCallback.setValidator(new DefaultTimestampValidator()); + + } + else if (callback instanceof CleanupCallback) { + SecurityContextHolder.clearContext(); + return; + } + throw new UnsupportedCallbackException(callback); + } + + private UserDetails loadUserDetails(String username) throws DataAccessException { + UserDetails user = userCache.getUserFromCache(username); + + if (user == null) { + try { + user = userDetailsService.loadUserByUsername(username); + } + catch (UsernameNotFoundException notFound) { + if (logger.isDebugEnabled()) { + logger.debug("Username '" + username + "' not found"); + } + return null; + } + userCache.putUserInCache(user); + } + return user; + } + + private class SpringSecurityDigestPasswordValidator extends PasswordValidationCallback.DigestPasswordValidator { + + private UserDetails user; + + private SpringSecurityDigestPasswordValidator(UserDetails user) { + this.user = user; + } + + public boolean validate(PasswordValidationCallback.Request request) + throws PasswordValidationCallback.PasswordValidationException { + if (super.validate(request)) { + UsernamePasswordAuthenticationToken authRequest = + new UsernamePasswordAuthenticationToken(user, user.getPassword()); + if (logger.isDebugEnabled()) { + logger.debug("Authentication success: " + authRequest.toString()); + } + + SecurityContextHolder.getContext().setAuthentication(authRequest); + return true; + } + else { + return false; + } + } + } + +} \ No newline at end of file diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityPlainTextPasswordValidationCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityPlainTextPasswordValidationCallbackHandler.java new file mode 100644 index 00000000..f1c24e39 --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityPlainTextPasswordValidationCallbackHandler.java @@ -0,0 +1,119 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed 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.springframework.ws.soap.security.xwss.callback.springsecurity; + +import java.io.IOException; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.UnsupportedCallbackException; + +import com.sun.xml.wss.impl.callback.PasswordValidationCallback; + +import org.springframework.security.Authentication; +import org.springframework.security.AuthenticationException; +import org.springframework.security.AuthenticationManager; +import org.springframework.security.context.SecurityContextHolder; +import org.springframework.security.providers.UsernamePasswordAuthenticationToken; +import org.springframework.util.Assert; +import org.springframework.ws.soap.security.callback.AbstractCallbackHandler; +import org.springframework.ws.soap.security.callback.CleanupCallback; + +/** + * Callback handler that validates a certificate uses an Spring Security AuthenticationManager. Logic based + * on Spring Security's BasicProcessingFilter. + *

+ * This handler requires an Spring Security AuthenticationManager to operate. It can be set using the + * authenticationManager property. An Spring Security UsernamePasswordAuthenticationToken is + * created with the username as principal and password as credentials. + *

+ * This class only handles PasswordValidationCallbacks that contain a + * PlainTextPasswordRequest, and throws an UnsupportedCallbackException for others. + * + * @author Arjen Poutsma + * @see org.springframework.security.providers.UsernamePasswordAuthenticationToken + * @see com.sun.xml.wss.impl.callback.PasswordValidationCallback + * @see com.sun.xml.wss.impl.callback.PasswordValidationCallback.PlainTextPasswordRequest + * @see org.springframework.security.ui.basicauth.BasicProcessingFilter + * @since 1.5.0 + */ +public class SpringSecurityPlainTextPasswordValidationCallbackHandler extends AbstractCallbackHandler { + + private AuthenticationManager authenticationManager; + + private boolean ignoreFailure = false; + + /** Sets the Spring Security authentication manager. Required. */ + public void setAuthenticationManager(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + public void setIgnoreFailure(boolean ignoreFailure) { + this.ignoreFailure = ignoreFailure; + } + + public void afterPropertiesSet() throws Exception { + Assert.notNull(authenticationManager, "authenticationManager is required"); + } + + /** + * Handles PasswordValidationCallbacks that contain a PlainTextPasswordRequest, and throws + * an UnsupportedCallbackException for others. + * + * @throws javax.security.auth.callback.UnsupportedCallbackException + * when the callback is not supported + */ + protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException { + if (callback instanceof PasswordValidationCallback) { + PasswordValidationCallback validationCallback = (PasswordValidationCallback) callback; + if (validationCallback.getRequest() instanceof PasswordValidationCallback.PlainTextPasswordRequest) { + validationCallback.setValidator(new SpringSecurityPlainTextPasswordValidator()); + return; + } + } + else if (callback instanceof CleanupCallback) { + SecurityContextHolder.clearContext(); + return; + } + throw new UnsupportedCallbackException(callback); + } + + private class SpringSecurityPlainTextPasswordValidator implements PasswordValidationCallback.PasswordValidator { + + public boolean validate(PasswordValidationCallback.Request request) + throws PasswordValidationCallback.PasswordValidationException { + PasswordValidationCallback.PlainTextPasswordRequest plainTextRequest = + (PasswordValidationCallback.PlainTextPasswordRequest) request; + try { + Authentication authResult = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken( + plainTextRequest.getUsername(), plainTextRequest.getPassword())); + if (logger.isDebugEnabled()) { + logger.debug("Authentication success: " + authResult.toString()); + } + SecurityContextHolder.getContext().setAuthentication(authResult); + return true; + } + catch (AuthenticationException failed) { + if (logger.isDebugEnabled()) { + logger.debug("Authentication request for user '" + plainTextRequest.getUsername() + "' failed: " + + failed.toString()); + } + SecurityContextHolder.clearContext(); + return ignoreFailure; + } + } + } + +} \ No newline at end of file diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityUsernamePasswordCallbackHandler.java b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityUsernamePasswordCallbackHandler.java new file mode 100644 index 00000000..0b91ec3e --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityUsernamePasswordCallbackHandler.java @@ -0,0 +1,69 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed 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.springframework.ws.soap.security.xwss.callback.springsecurity; + +import java.io.IOException; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.UnsupportedCallbackException; + +import com.sun.xml.wss.impl.callback.PasswordCallback; +import com.sun.xml.wss.impl.callback.UsernameCallback; + +import org.springframework.security.Authentication; +import org.springframework.security.context.SecurityContextHolder; +import org.springframework.ws.soap.security.callback.AbstractCallbackHandler; + +/** + * Callback handler that adds username/password information to a mesage using an Spring Security {@link + * org.springframework.security.context.SecurityContext}. + *

+ * This class handles UsernameCallbacks and PasswordCallbacks, and throws an + * UnsupportedCallbackException for others + * + * @author Arjen Poutsma + * @since 1.5.0 + */ +public class SpringSecurityUsernamePasswordCallbackHandler extends AbstractCallbackHandler { + + protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException { + if (callback instanceof UsernameCallback) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null && authentication.getName() != null) { + UsernameCallback usernameCallback = (UsernameCallback) callback; + usernameCallback.setUsername(authentication.getName()); + return; + } + else { + logger.warn( + "Cannot handle UsernameCallback: Spring Security SecurityContext contains no Authentication"); + } + } + else if (callback instanceof PasswordCallback) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null && authentication.getName() != null) { + PasswordCallback passwordCallback = (PasswordCallback) callback; + passwordCallback.setPassword(authentication.getCredentials().toString()); + return; + } + else { + logger.warn( + "Canot handle PasswordCallback: Spring Security SecurityContext contains no Authentication"); + } + } + throw new UnsupportedCallbackException(callback); + } +} \ No newline at end of file diff --git a/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/package.html b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/package.html new file mode 100644 index 00000000..d826fe48 --- /dev/null +++ b/security/src/main/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/package.html @@ -0,0 +1,6 @@ + + +Contains CallbackHandler implementations for XWSS that use +Spring Security. + + \ No newline at end of file diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorSpringSecurityCallbackHandlerTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorSpringSecurityCallbackHandlerTest.java new file mode 100755 index 00000000..4e46b5b5 --- /dev/null +++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/AxiomWss4jMessageInterceptorSpringSecurityCallbackHandlerTest.java @@ -0,0 +1,6 @@ +package org.springframework.ws.soap.security.wss4j; + +public class AxiomWss4jMessageInterceptorSpringSecurityCallbackHandlerTest + extends Wss4jMessageInterceptorSpringSecurityCallbackHandlerTestCase { + +} \ No newline at end of file diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorSpringSecurityCallbackHandlerTest.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorSpringSecurityCallbackHandlerTest.java new file mode 100755 index 00000000..d740f7e0 --- /dev/null +++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/SaajWss4jMessageInterceptorSpringSecurityCallbackHandlerTest.java @@ -0,0 +1,6 @@ +package org.springframework.ws.soap.security.wss4j; + +public class SaajWss4jMessageInterceptorSpringSecurityCallbackHandlerTest + extends Wss4jMessageInterceptorSpringSecurityCallbackHandlerTestCase { + +} \ No newline at end of file diff --git a/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorSpringSecurityCallbackHandlerTestCase.java b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorSpringSecurityCallbackHandlerTestCase.java new file mode 100755 index 00000000..9ef12e26 --- /dev/null +++ b/security/src/test/java/org/springframework/ws/soap/security/wss4j/Wss4jMessageInterceptorSpringSecurityCallbackHandlerTestCase.java @@ -0,0 +1,110 @@ +package org.springframework.ws.soap.security.wss4j; + +import java.util.Properties; + +import org.apache.ws.security.WSConstants; +import org.easymock.MockControl; + +import org.springframework.security.Authentication; +import org.springframework.security.AuthenticationManager; +import org.springframework.security.GrantedAuthority; +import org.springframework.security.context.SecurityContextHolder; +import org.springframework.security.providers.TestingAuthenticationToken; +import org.springframework.security.providers.UsernamePasswordAuthenticationToken; +import org.springframework.security.userdetails.memory.InMemoryDaoImpl; +import org.springframework.ws.context.DefaultMessageContext; +import org.springframework.ws.context.MessageContext; +import org.springframework.ws.server.EndpointInterceptor; +import org.springframework.ws.soap.SoapMessage; +import org.springframework.ws.soap.security.wss4j.callback.springsecurity.SpringSecurityDigestPasswordValidationCallbackHandler; +import org.springframework.ws.soap.security.wss4j.callback.springsecurity.SpringSecurityPlainTextPasswordValidationCallbackHandler; + +public abstract class Wss4jMessageInterceptorSpringSecurityCallbackHandlerTestCase extends Wss4jTestCase { + + private Properties users = new Properties(); + + private MockControl control; + + private AuthenticationManager mock; + + protected void onSetup() throws Exception { + control = MockControl.createControl(AuthenticationManager.class); + mock = (AuthenticationManager) control.getMock(); + users.setProperty("Bert", "Ernie,ROLE_TEST"); + } + + protected void tearDown() throws Exception { + control.verify(); + SecurityContextHolder.clearContext(); + } + + public void testValidateUsernameTokenPlainText() throws Exception { + EndpointInterceptor interceptor = prepareInterceptor("UsernameToken", true, false); + SoapMessage message = loadMessage("usernameTokenPlainText-soap.xml"); + MessageContext messageContext = new DefaultMessageContext(message, getMessageFactory()); + interceptor.handleRequest(messageContext, null); + assertValidateUsernameToken(message); + + // test clean up + messageContext.getResponse(); + interceptor.handleResponse(messageContext, null); + assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication()); + } + + public void testValidateUsernameTokenDigest() throws Exception { + EndpointInterceptor interceptor = prepareInterceptor("UsernameToken", true, true); + SoapMessage message = loadMessage("usernameTokenDigest-soap.xml"); + MessageContext messageContext = new DefaultMessageContext(message, getMessageFactory()); + interceptor.handleRequest(messageContext, null); + assertValidateUsernameToken(message); + + // test clean up + messageContext.getResponse(); + interceptor.handleResponse(messageContext, null); + assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication()); + } + + protected void assertValidateUsernameToken(SoapMessage message) throws Exception { + Object result = getMessage(message); + assertNotNull("No result returned", result); + assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security", + getDocument(message)); + assertNotNull("No Authentication created", SecurityContextHolder.getContext().getAuthentication()); + } + + protected EndpointInterceptor prepareInterceptor(String actions, boolean validating, boolean digest) + throws Exception { + Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor(); + if (validating) { + interceptor.setValidationActions(actions); + } + else { + interceptor.setSecurementActions(actions); + } + if (digest) { + SpringSecurityDigestPasswordValidationCallbackHandler callbackHandler = + new SpringSecurityDigestPasswordValidationCallbackHandler(); + InMemoryDaoImpl userDetailsService = new InMemoryDaoImpl(); + userDetailsService.setUserProperties(users); + userDetailsService.afterPropertiesSet(); + callbackHandler.setUserDetailsService(userDetailsService); + interceptor.setSecurementPasswordType(WSConstants.PW_DIGEST); + interceptor.setValidationCallbackHandler(callbackHandler); + interceptor.afterPropertiesSet(); + } + else { + SpringSecurityPlainTextPasswordValidationCallbackHandler callbackHandler = + new SpringSecurityPlainTextPasswordValidationCallbackHandler(); + Authentication authResult = new TestingAuthenticationToken("Bert", "Ernie", new GrantedAuthority[0]); + control.expectAndReturn(mock.authenticate(new UsernamePasswordAuthenticationToken("Bert", "Ernie")), + authResult); + callbackHandler.setAuthenticationManager(mock); + callbackHandler.afterPropertiesSet(); + interceptor.setSecurementPasswordType(WSConstants.PW_TEXT); + interceptor.setValidationCallbackHandler(callbackHandler); + interceptor.afterPropertiesSet(); + } + control.replay(); + return interceptor; + } +} \ No newline at end of file diff --git a/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityCertificateValidationCallbackHandlerTest.java b/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityCertificateValidationCallbackHandlerTest.java new file mode 100644 index 00000000..b11c885c --- /dev/null +++ b/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityCertificateValidationCallbackHandlerTest.java @@ -0,0 +1,106 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed 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.springframework.ws.soap.security.xwss.callback.springsecurity; + +import java.io.InputStream; +import java.security.KeyStore; +import java.security.cert.X509Certificate; + +import com.sun.xml.wss.impl.callback.CertificateValidationCallback; +import junit.framework.TestCase; +import org.easymock.MockControl; + +import org.springframework.core.io.ClassPathResource; +import org.springframework.security.AuthenticationManager; +import org.springframework.security.BadCredentialsException; +import org.springframework.security.GrantedAuthority; +import org.springframework.security.context.SecurityContextHolder; +import org.springframework.security.providers.TestingAuthenticationToken; +import org.springframework.security.providers.x509.X509AuthenticationToken; +import org.springframework.ws.soap.security.callback.CleanupCallback; + +public class SpringSecurityCertificateValidationCallbackHandlerTest extends TestCase { + + private SpringSecurityCertificateValidationCallbackHandler callbackHandler; + + private MockControl control; + + private AuthenticationManager mock; + + private X509Certificate certificate; + + private CertificateValidationCallback callback; + + protected void setUp() throws Exception { + callbackHandler = new SpringSecurityCertificateValidationCallbackHandler(); + control = MockControl.createControl(AuthenticationManager.class); + mock = (AuthenticationManager) control.getMock(); + callbackHandler.setAuthenticationManager(mock); + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + InputStream is = null; + try { + is = new ClassPathResource("/org/springframework/ws/soap/security/xwss/test-keystore.jks").getInputStream(); + keyStore.load(is, "password".toCharArray()); + } + finally { + if (is != null) { + is.close(); + } + } + certificate = (X509Certificate) keyStore.getCertificate("alias"); + callback = new CertificateValidationCallback(certificate); + } + + protected void tearDown() throws Exception { + SecurityContextHolder.clearContext(); + } + + public void testValidateCertificateValid() throws Exception { + mock.authenticate(new X509AuthenticationToken(certificate)); + control.setMatcher(MockControl.ALWAYS_MATCHER); + control.setReturnValue(new TestingAuthenticationToken(certificate, null, new GrantedAuthority[0])); + control.replay(); + callbackHandler.handleInternal(callback); + boolean authenticated = callback.getResult(); + assertTrue("Not authenticated", authenticated); + assertNotNull("No Authentication created", SecurityContextHolder.getContext().getAuthentication()); + control.verify(); + } + + public void testValidateCertificateInvalid() throws Exception { + mock.authenticate(new X509AuthenticationToken(certificate)); + control.setMatcher(MockControl.ALWAYS_MATCHER); + control.setThrowable(new BadCredentialsException("")); + control.replay(); + callbackHandler.handleInternal(callback); + boolean authenticated = callback.getResult(); + assertFalse("Authenticated", authenticated); + assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication()); + control.verify(); + } + + public void testCleanUp() throws Exception { + TestingAuthenticationToken authentication = + new TestingAuthenticationToken(new Object(), new Object(), new GrantedAuthority[0]); + SecurityContextHolder.getContext().setAuthentication(authentication); + + CleanupCallback cleanupCallback = new CleanupCallback(); + callbackHandler.handleInternal(cleanupCallback); + assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication()); + } + +} \ No newline at end of file diff --git a/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityDigestPasswordValidationCallbackHandlerTest.java b/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityDigestPasswordValidationCallbackHandlerTest.java new file mode 100644 index 00000000..76d7be80 --- /dev/null +++ b/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityDigestPasswordValidationCallbackHandlerTest.java @@ -0,0 +1,106 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed 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.springframework.ws.soap.security.xwss.callback.springsecurity; + +import com.sun.xml.wss.impl.callback.PasswordValidationCallback; +import junit.framework.TestCase; +import org.easymock.MockControl; + +import org.springframework.security.GrantedAuthority; +import org.springframework.security.context.SecurityContextHolder; +import org.springframework.security.providers.TestingAuthenticationToken; +import org.springframework.security.userdetails.User; +import org.springframework.security.userdetails.UserDetailsService; +import org.springframework.security.userdetails.UsernameNotFoundException; +import org.springframework.ws.soap.security.callback.CleanupCallback; + +public class SpringSecurityDigestPasswordValidationCallbackHandlerTest extends TestCase { + + private SpringSecurityDigestPasswordValidationCallbackHandler callbackHandler; + + private MockControl control; + + private UserDetailsService mock; + + private String username; + + private String password; + + private PasswordValidationCallback callback; + + protected void setUp() throws Exception { + callbackHandler = new SpringSecurityDigestPasswordValidationCallbackHandler(); + control = MockControl.createControl(UserDetailsService.class); + mock = (UserDetailsService) control.getMock(); + callbackHandler.setUserDetailsService(mock); + username = "Bert"; + password = "Ernie"; + String nonce = "9mdsYDCrjjYRur0rxzYt2oD7"; + String passwordDigest = "kwNstEaiFOrI7B31j7GuETYvdgk="; + String creationTime = "2006-06-01T23:48:42Z"; + PasswordValidationCallback.DigestPasswordRequest request = + new PasswordValidationCallback.DigestPasswordRequest(username, passwordDigest, nonce, creationTime); + callback = new PasswordValidationCallback(request); + } + + protected void tearDown() throws Exception { + SecurityContextHolder.clearContext(); + } + + public void testAuthenticateUserDigestUserNotFound() throws Exception { + control.expectAndThrow(mock.loadUserByUsername(username), new UsernameNotFoundException(username)); + control.replay(); + callbackHandler.handleInternal(callback); + boolean authenticated = callback.getResult(); + assertFalse("Authenticated", authenticated); + assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication()); + control.verify(); + } + + public void testAuthenticateUserDigestValid() throws Exception { + User user = new User(username, password, true, true, true, true, new GrantedAuthority[0]); + control.expectAndReturn(mock.loadUserByUsername(username), user); + control.replay(); + callbackHandler.handleInternal(callback); + boolean authenticated = callback.getResult(); + assertTrue("Not authenticated", authenticated); + assertNotNull("No Authentication created", SecurityContextHolder.getContext().getAuthentication()); + control.verify(); + } + + public void testAuthenticateUserDigestValidInvalid() throws Exception { + User user = new User(username, "Big bird", true, true, true, true, new GrantedAuthority[0]); + control.expectAndReturn(mock.loadUserByUsername(username), user); + control.replay(); + callbackHandler.handleInternal(callback); + boolean authenticated = callback.getResult(); + assertFalse("Authenticated", authenticated); + assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication()); + control.verify(); + } + + public void testCleanUp() throws Exception { + TestingAuthenticationToken authentication = + new TestingAuthenticationToken(new Object(), new Object(), new GrantedAuthority[0]); + SecurityContextHolder.getContext().setAuthentication(authentication); + + CleanupCallback cleanupCallback = new CleanupCallback(); + callbackHandler.handleInternal(cleanupCallback); + assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication()); + } + +} \ No newline at end of file diff --git a/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityPlainTextPasswordValidationCallbackHandlerTest.java b/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityPlainTextPasswordValidationCallbackHandlerTest.java new file mode 100644 index 00000000..c281d929 --- /dev/null +++ b/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityPlainTextPasswordValidationCallbackHandlerTest.java @@ -0,0 +1,95 @@ +/* + * Copyright 2008 the original author or authors. + * + * Licensed 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.springframework.ws.soap.security.xwss.callback.springsecurity; + +import com.sun.xml.wss.impl.callback.PasswordValidationCallback; +import junit.framework.TestCase; +import org.easymock.MockControl; + +import org.springframework.security.Authentication; +import org.springframework.security.AuthenticationManager; +import org.springframework.security.BadCredentialsException; +import org.springframework.security.GrantedAuthority; +import org.springframework.security.context.SecurityContextHolder; +import org.springframework.security.providers.TestingAuthenticationToken; +import org.springframework.security.providers.UsernamePasswordAuthenticationToken; +import org.springframework.ws.soap.security.callback.CleanupCallback; + +public class SpringSecurityPlainTextPasswordValidationCallbackHandlerTest extends TestCase { + + private SpringSecurityPlainTextPasswordValidationCallbackHandler callbackHandler; + + private MockControl control; + + private AuthenticationManager mock; + + private PasswordValidationCallback callback; + + private String username; + + private String password; + + protected void setUp() throws Exception { + callbackHandler = new SpringSecurityPlainTextPasswordValidationCallbackHandler(); + control = MockControl.createControl(AuthenticationManager.class); + mock = (AuthenticationManager) control.getMock(); + callbackHandler.setAuthenticationManager(mock); + username = "Bert"; + password = "Ernie"; + PasswordValidationCallback.PlainTextPasswordRequest request = + new PasswordValidationCallback.PlainTextPasswordRequest(username, password); + callback = new PasswordValidationCallback(request); + } + + protected void tearDown() throws Exception { + SecurityContextHolder.clearContext(); + } + + public void testAuthenticateUserPlainTextValid() throws Exception { + Authentication authResult = new TestingAuthenticationToken(username, password, new GrantedAuthority[0]); + control.expectAndReturn(mock.authenticate(new UsernamePasswordAuthenticationToken(username, password)), + authResult); + control.replay(); + callbackHandler.handleInternal(callback); + boolean authenticated = callback.getResult(); + assertTrue("Not authenticated", authenticated); + assertNotNull("No Authentication created", SecurityContextHolder.getContext().getAuthentication()); + control.verify(); + } + + public void testAuthenticateUserPlainTextInvalid() throws Exception { + control.expectAndThrow(mock.authenticate(new UsernamePasswordAuthenticationToken(username, password)), + new BadCredentialsException("")); + control.replay(); + callbackHandler.handleInternal(callback); + boolean authenticated = callback.getResult(); + assertFalse("Authenticated", authenticated); + assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication()); + control.verify(); + } + + public void testCleanUp() throws Exception { + TestingAuthenticationToken authentication = + new TestingAuthenticationToken(new Object(), new Object(), new GrantedAuthority[0]); + SecurityContextHolder.getContext().setAuthentication(authentication); + + CleanupCallback cleanupCallback = new CleanupCallback(); + callbackHandler.handleInternal(cleanupCallback); + assertNull("Authentication created", SecurityContextHolder.getContext().getAuthentication()); + } + +} \ No newline at end of file diff --git a/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityUsernamePasswordCallbackHandlerTest.java b/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityUsernamePasswordCallbackHandlerTest.java new file mode 100644 index 00000000..a069b9c8 --- /dev/null +++ b/security/src/test/java/org/springframework/ws/soap/security/xwss/callback/springsecurity/SpringSecurityUsernamePasswordCallbackHandlerTest.java @@ -0,0 +1,52 @@ +/* + * Copyright ${YEAR} the original author or authors. + * + * Licensed 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.springframework.ws.soap.security.xwss.callback.springsecurity; + +import com.sun.xml.wss.impl.callback.PasswordCallback; +import com.sun.xml.wss.impl.callback.UsernameCallback; +import junit.framework.TestCase; + +import org.springframework.security.Authentication; +import org.springframework.security.context.SecurityContextHolder; +import org.springframework.security.providers.UsernamePasswordAuthenticationToken; + +public class SpringSecurityUsernamePasswordCallbackHandlerTest extends TestCase { + + private SpringSecurityUsernamePasswordCallbackHandler handler; + + protected void setUp() throws Exception { + handler = new SpringSecurityUsernamePasswordCallbackHandler(); + Authentication authentication = new UsernamePasswordAuthenticationToken("Bert", "Ernie"); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + protected void tearDown() throws Exception { + SecurityContextHolder.clearContext(); + } + + public void testUsernameCallback() throws Exception { + UsernameCallback usernameCallback = new UsernameCallback(); + handler.handleInternal(usernameCallback); + assertEquals("Invalid username", "Bert", usernameCallback.getUsername()); + } + + public void testPasswordCallback() throws Exception { + PasswordCallback passwordCallback = new PasswordCallback(); + handler.handleInternal(passwordCallback); + assertEquals("Invalid username", "Ernie", passwordCallback.getPassword()); + } +} \ No newline at end of file