Skip to content

Commit 470cc1f

Browse files
committed
SWS-252
1 parent 521151d commit 470cc1f

15 files changed

+1162
-2
lines changed

security/pom.xml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
23
<parent>
34
<artifactId>spring-ws-parent</artifactId>
45
<groupId>org.springframework.ws</groupId>
@@ -21,6 +22,12 @@
2122
<name>Spring External Dependencies Repository</name>
2223
<url>https://springframework.svn.sourceforge.net/svnroot/springframework/repos/repo-ext/</url>
2324
</repository>
25+
<!-- S3 repo required for Spring Security Milestones -->
26+
<repository>
27+
<id>spring-s3</id>
28+
<name>Springframework Maven SNAPSHOT Repository</name>
29+
<url>http://s3.amazonaws.com/maven.springframework.org/milestone</url>
30+
</repository>
2431
<repository>
2532
<id>wso2</id>
2633
<name>WSO2 Repository</name>
@@ -136,6 +143,12 @@
136143
<groupId>org.acegisecurity</groupId>
137144
<artifactId>acegi-security</artifactId>
138145
</dependency>
146+
<!-- Spring Security dependencies -->
147+
<dependency>
148+
<groupId>org.springframework.security</groupId>
149+
<artifactId>spring-security-core</artifactId>
150+
<optional>true</optional>
151+
</dependency>
139152
<!-- JEE dependencies -->
140153
<dependency>
141154
<groupId>javax.mail</groupId>
@@ -149,4 +162,4 @@
149162
<scope>test</scope>
150163
</dependency>
151164
</dependencies>
152-
</project>
165+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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.wss4j.callback.springsecurity;
18+
19+
import java.io.IOException;
20+
import javax.security.auth.callback.UnsupportedCallbackException;
21+
22+
import org.apache.ws.security.WSPasswordCallback;
23+
import org.apache.ws.security.WSUsernameTokenPrincipal;
24+
25+
import org.springframework.dao.DataAccessException;
26+
import org.springframework.security.context.SecurityContextHolder;
27+
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
28+
import org.springframework.security.providers.dao.UserCache;
29+
import org.springframework.security.providers.dao.cache.NullUserCache;
30+
import org.springframework.security.userdetails.UserDetails;
31+
import org.springframework.security.userdetails.UserDetailsService;
32+
import org.springframework.security.userdetails.UsernameNotFoundException;
33+
import org.springframework.util.Assert;
34+
import org.springframework.ws.soap.security.callback.CleanupCallback;
35+
import org.springframework.ws.soap.security.wss4j.callback.AbstractWsPasswordCallbackHandler;
36+
import org.springframework.ws.soap.security.wss4j.callback.UsernameTokenPrincipalCallback;
37+
38+
/**
39+
* Callback handler that validates a password digest using an Spring Security <code>UserDetailsService</code>. Logic
40+
* based on Spring Security's <code>DigestProcessingFilter</code>.
41+
* <p/>
42+
* An Spring Security <code>UserDetailService</code> is used to load <code>UserDetails</code> from. The digest of the
43+
* password contained in this details object is then compared with the digest in the message.
44+
*
45+
* @author Arjen Poutsma
46+
* @see org.springframework.security.userdetails.UserDetailsService
47+
* @see org.springframework.security.ui.digestauth.DigestProcessingFilter
48+
* @since 1.5.0
49+
*/
50+
public class SpringSecurityDigestPasswordValidationCallbackHandler extends AbstractWsPasswordCallbackHandler {
51+
52+
private UserCache userCache = new NullUserCache();
53+
54+
private UserDetailsService userDetailsService;
55+
56+
/** Sets the users cache. Not required, but can benefit performance. */
57+
public void setUserCache(UserCache userCache) {
58+
this.userCache = userCache;
59+
}
60+
61+
/** Sets the Spring Security user details service. Required. */
62+
public void setUserDetailsService(UserDetailsService userDetailsService) {
63+
this.userDetailsService = userDetailsService;
64+
}
65+
66+
public void afterPropertiesSet() throws Exception {
67+
Assert.notNull(userDetailsService, "userDetailsService is required");
68+
}
69+
70+
protected void handleUsernameToken(WSPasswordCallback callback) throws IOException, UnsupportedCallbackException {
71+
String identifier = callback.getIdentifer();
72+
UserDetails user = loadUserDetails(identifier);
73+
if (user != null) {
74+
callback.setPassword(user.getPassword());
75+
}
76+
}
77+
78+
protected void handleUsernameTokenPrincipal(UsernameTokenPrincipalCallback callback)
79+
throws IOException, UnsupportedCallbackException {
80+
WSUsernameTokenPrincipal principal = callback.getPrincipal();
81+
UsernamePasswordAuthenticationToken authRequest =
82+
new UsernamePasswordAuthenticationToken(principal, principal.getPassword());
83+
if (logger.isDebugEnabled()) {
84+
logger.debug("Authentication success: " + authRequest.toString());
85+
}
86+
SecurityContextHolder.getContext().setAuthentication(authRequest);
87+
}
88+
89+
protected void handleCleanup(CleanupCallback callback) throws IOException, UnsupportedCallbackException {
90+
SecurityContextHolder.clearContext();
91+
}
92+
93+
private UserDetails loadUserDetails(String username) throws DataAccessException {
94+
UserDetails user = userCache.getUserFromCache(username);
95+
96+
if (user == null) {
97+
try {
98+
user = userDetailsService.loadUserByUsername(username);
99+
}
100+
catch (UsernameNotFoundException notFound) {
101+
if (logger.isDebugEnabled()) {
102+
logger.debug("Username '" + username + "' not found");
103+
}
104+
return null;
105+
}
106+
userCache.putUserInCache(user);
107+
}
108+
return user;
109+
}
110+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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.wss4j.callback.springsecurity;
18+
19+
import java.io.IOException;
20+
import javax.security.auth.callback.UnsupportedCallbackException;
21+
22+
import org.apache.ws.security.WSPasswordCallback;
23+
import org.apache.ws.security.WSSecurityException;
24+
25+
import org.springframework.security.Authentication;
26+
import org.springframework.security.AuthenticationException;
27+
import org.springframework.security.AuthenticationManager;
28+
import org.springframework.security.context.SecurityContextHolder;
29+
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
30+
import org.springframework.util.Assert;
31+
import org.springframework.ws.soap.security.callback.CleanupCallback;
32+
import org.springframework.ws.soap.security.wss4j.callback.AbstractWsPasswordCallbackHandler;
33+
34+
/**
35+
* Callback handler that validates a certificate uses an Spring Security <code>AuthenticationManager</code>. Logic based
36+
* on Spring Security's <code>BasicProcessingFilter</code>.
37+
* <p/>
38+
* This handler requires an Spring Security <code>AuthenticationManager</code> to operate. It can be set using the
39+
* <code>authenticationManager</code> property. An Spring Security <code>UsernamePasswordAuthenticationToken</code> is
40+
* created with the username as principal and password as credentials.
41+
*
42+
* @author Arjen Poutsma
43+
* @see org.springframework.security.providers.UsernamePasswordAuthenticationToken
44+
* @see org.springframework.security.ui.basicauth.BasicProcessingFilter
45+
* @since 1.5.0
46+
*/
47+
public class SpringSecurityPlainTextPasswordValidationCallbackHandler extends AbstractWsPasswordCallbackHandler {
48+
49+
private AuthenticationManager authenticationManager;
50+
51+
private boolean ignoreFailure = false;
52+
53+
/** Sets the Spring Security authentication manager. Required. */
54+
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
55+
this.authenticationManager = authenticationManager;
56+
}
57+
58+
public void setIgnoreFailure(boolean ignoreFailure) {
59+
this.ignoreFailure = ignoreFailure;
60+
}
61+
62+
public void afterPropertiesSet() throws Exception {
63+
Assert.notNull(authenticationManager, "authenticationManager is required");
64+
}
65+
66+
protected void handleCleanup(CleanupCallback callback) throws IOException, UnsupportedCallbackException {
67+
SecurityContextHolder.clearContext();
68+
}
69+
70+
protected void handleUsernameTokenUnknown(WSPasswordCallback callback)
71+
throws IOException, UnsupportedCallbackException {
72+
String identifier = callback.getIdentifer();
73+
try {
74+
Authentication authResult = authenticationManager
75+
.authenticate(new UsernamePasswordAuthenticationToken(identifier, callback.getPassword()));
76+
if (logger.isDebugEnabled()) {
77+
logger.debug("Authentication success: " + authResult.toString());
78+
}
79+
SecurityContextHolder.getContext().setAuthentication(authResult);
80+
}
81+
catch (AuthenticationException failed) {
82+
if (logger.isDebugEnabled()) {
83+
logger.debug("Authentication request for user '" + identifier + "' failed: " + failed.toString());
84+
}
85+
SecurityContextHolder.clearContext();
86+
if (!ignoreFailure) {
87+
throw new WSSecurityException(WSSecurityException.FAILED_AUTHENTICATION);
88+
}
89+
}
90+
}
91+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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.springsecurity;
18+
19+
import java.io.IOException;
20+
import java.security.cert.X509Certificate;
21+
import javax.security.auth.callback.Callback;
22+
import javax.security.auth.callback.UnsupportedCallbackException;
23+
24+
import com.sun.xml.wss.impl.callback.CertificateValidationCallback;
25+
26+
import org.springframework.security.Authentication;
27+
import org.springframework.security.AuthenticationException;
28+
import org.springframework.security.AuthenticationManager;
29+
import org.springframework.security.context.SecurityContextHolder;
30+
import org.springframework.security.providers.x509.X509AuthenticationToken;
31+
import org.springframework.util.Assert;
32+
import org.springframework.ws.soap.security.callback.AbstractCallbackHandler;
33+
import org.springframework.ws.soap.security.callback.CleanupCallback;
34+
35+
/**
36+
* Callback handler that validates a certificate using an Spring Security <code>AuthenticationManager</code>. Logic
37+
* based on Spring Security's <code>X509ProcessingFilter</code>. <p/> Spring Security
38+
* <code>X509AuthenticationToken</code> is created with the certificate as the credentials. <p/> The configured
39+
* authentication manager is expected to supply a provider which can handle this token (usually an instance of
40+
* <code>X509AuthenticationProvider</code>).</p>
41+
* <p/>
42+
* This class only handles <code>CertificateValidationCallback</code>s, and throws an
43+
* <code>UnsupportedCallbackException</code> for others.
44+
*
45+
* @author Arjen Poutsma
46+
* @see org.springframework.security.providers.x509.X509AuthenticationToken
47+
* @see org.springframework.security.providers.x509.X509AuthenticationProvider
48+
* @see org.springframework.security.ui.x509.X509ProcessingFilter
49+
* @see com.sun.xml.wss.impl.callback.CertificateValidationCallback
50+
* @since 1.5.0
51+
*/
52+
public class SpringSecurityCertificateValidationCallbackHandler extends AbstractCallbackHandler {
53+
54+
private AuthenticationManager authenticationManager;
55+
56+
private boolean ignoreFailure = false;
57+
58+
/** Sets the Spring Security authentication manager. Required. */
59+
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
60+
this.authenticationManager = authenticationManager;
61+
}
62+
63+
public void setIgnoreFailure(boolean ignoreFailure) {
64+
this.ignoreFailure = ignoreFailure;
65+
}
66+
67+
public void afterPropertiesSet() throws Exception {
68+
Assert.notNull(authenticationManager, "authenticationManager is required");
69+
}
70+
71+
/**
72+
* Handles <code>CertificateValidationCallback</code>s, and throws an <code>UnsupportedCallbackException</code> for
73+
* others
74+
*
75+
* @throws javax.security.auth.callback.UnsupportedCallbackException
76+
* when the callback is not supported
77+
*/
78+
protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
79+
if (callback instanceof CertificateValidationCallback) {
80+
((CertificateValidationCallback) callback).setValidator(new SpringSecurityCertificateValidator());
81+
}
82+
else if (callback instanceof CleanupCallback) {
83+
SecurityContextHolder.clearContext();
84+
}
85+
else {
86+
throw new UnsupportedCallbackException(callback);
87+
}
88+
}
89+
90+
private class SpringSecurityCertificateValidator implements CertificateValidationCallback.CertificateValidator {
91+
92+
public boolean validate(X509Certificate certificate)
93+
throws CertificateValidationCallback.CertificateValidationException {
94+
boolean result;
95+
try {
96+
Authentication authResult =
97+
authenticationManager.authenticate(new X509AuthenticationToken(certificate));
98+
if (logger.isDebugEnabled()) {
99+
logger.debug("Authentication request for certificate with DN [" +
100+
certificate.getSubjectX500Principal().getName() + "] successful");
101+
}
102+
SecurityContextHolder.getContext().setAuthentication(authResult);
103+
return true;
104+
}
105+
catch (AuthenticationException failed) {
106+
if (logger.isDebugEnabled()) {
107+
logger.debug("Authentication request for certificate with DN [" +
108+
certificate.getSubjectX500Principal().getName() + "] failed: " + failed.toString());
109+
}
110+
SecurityContextHolder.clearContext();
111+
result = ignoreFailure;
112+
}
113+
return result;
114+
}
115+
}
116+
}

0 commit comments

Comments
 (0)