001: /*
002: * Copyright 2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.ws.soap.security.xwss.callback.acegi;
018:
019: import java.io.IOException;
020: import javax.security.auth.callback.Callback;
021: import javax.security.auth.callback.UnsupportedCallbackException;
022:
023: import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
024: import com.sun.xml.wss.impl.callback.TimestampValidationCallback;
025: import org.acegisecurity.context.SecurityContextHolder;
026: import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
027: import org.acegisecurity.providers.dao.UserCache;
028: import org.acegisecurity.providers.dao.cache.NullUserCache;
029: import org.acegisecurity.userdetails.UserDetails;
030: import org.acegisecurity.userdetails.UserDetailsService;
031: import org.acegisecurity.userdetails.UsernameNotFoundException;
032: import org.springframework.dao.DataAccessException;
033: import org.springframework.util.Assert;
034: import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
035: import org.springframework.ws.soap.security.xwss.callback.DefaultTimestampValidator;
036:
037: /**
038: * Callback handler that validates a password digest using an Acegi <code>UserDetailsService</code>. Logic based on
039: * Acegi's <code>DigestProcessingFilter</code>.
040: * <p/>
041: * An Acegi <code>UserDetailService</code> is used to load <code>UserDetails</code> from. The digest of the password
042: * contained in this details object is then compared with the digest in the message.
043: * <p/>
044: * This class only handles <code>PasswordValidationCallback</code>s that contain a <code>DigestPasswordRequest</code>,
045: * and throws an <code>UnsupportedCallbackException</code> for others.
046: *
047: * @author Arjen Poutsma
048: * @see UserDetailsService
049: * @see PasswordValidationCallback
050: * @see com.sun.xml.wss.impl.callback.PasswordValidationCallback.DigestPasswordRequest
051: * @see org.acegisecurity.ui.digestauth.DigestProcessingFilter
052: */
053: public class AcegiDigestPasswordValidationCallbackHandler extends
054: AbstractCallbackHandler {
055:
056: private UserCache userCache = new NullUserCache();
057:
058: private UserDetailsService userDetailsService;
059:
060: /** Sets the users cache. Not required, but can benefit performance. */
061: public void setUserCache(UserCache userCache) {
062: this .userCache = userCache;
063: }
064:
065: /** Sets the Acegi user details service. Required. */
066: public void setUserDetailsService(
067: UserDetailsService userDetailsService) {
068: this .userDetailsService = userDetailsService;
069: }
070:
071: public void afterPropertiesSet() throws Exception {
072: Assert.notNull(userDetailsService,
073: "userDetailsService is required");
074: }
075:
076: /**
077: * Handles <code>PasswordValidationCallback</code>s that contain a <code>DigestPasswordRequest</code>, and throws an
078: * <code>UnsupportedCallbackException</code> for others
079: *
080: * @throws UnsupportedCallbackException when the callback is not supported
081: */
082: protected void handleInternal(Callback callback)
083: throws IOException, UnsupportedCallbackException {
084: if (callback instanceof PasswordValidationCallback) {
085: PasswordValidationCallback passwordCallback = (PasswordValidationCallback) callback;
086: if (passwordCallback.getRequest() instanceof PasswordValidationCallback.DigestPasswordRequest) {
087: PasswordValidationCallback.DigestPasswordRequest request = (PasswordValidationCallback.DigestPasswordRequest) passwordCallback
088: .getRequest();
089: String username = request.getUsername();
090: UserDetails user = loadUserDetails(username);
091: if (user != null) {
092: request.setPassword(user.getPassword());
093: }
094: AcegiDigestPasswordValidator validator = new AcegiDigestPasswordValidator(
095: user);
096: passwordCallback.setValidator(validator);
097: return;
098: }
099: } else if (callback instanceof TimestampValidationCallback) {
100: TimestampValidationCallback timestampCallback = (TimestampValidationCallback) callback;
101: timestampCallback
102: .setValidator(new DefaultTimestampValidator());
103:
104: }
105: throw new UnsupportedCallbackException(callback);
106: }
107:
108: private UserDetails loadUserDetails(String username)
109: throws DataAccessException {
110: UserDetails user = userCache.getUserFromCache(username);
111:
112: if (user == null) {
113: try {
114: user = userDetailsService.loadUserByUsername(username);
115: } catch (UsernameNotFoundException notFound) {
116: if (logger.isDebugEnabled()) {
117: logger.debug("Username '" + username
118: + "' not found");
119: }
120: return null;
121: }
122: userCache.putUserInCache(user);
123: }
124: return user;
125: }
126:
127: private class AcegiDigestPasswordValidator extends
128: PasswordValidationCallback.DigestPasswordValidator {
129:
130: private UserDetails user;
131:
132: private AcegiDigestPasswordValidator(UserDetails user) {
133: this .user = user;
134: }
135:
136: public boolean validate(
137: PasswordValidationCallback.Request request)
138: throws PasswordValidationCallback.PasswordValidationException {
139: if (super .validate(request)) {
140: UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
141: user, user.getPassword());
142: if (logger.isDebugEnabled()) {
143: logger.debug("Authentication success: "
144: + authRequest.toString());
145: }
146:
147: SecurityContextHolder.getContext().setAuthentication(
148: authRequest);
149: return true;
150: } else {
151: return false;
152: }
153: }
154: }
155:
156: }
|