001: /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
002: *
003: * Licensed under the Apache License, Version 2.0 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015:
016: package org.acegisecurity.providers.dao;
017:
018: import java.util.Map;
019:
020: import org.acegisecurity.AuthenticationException;
021: import org.acegisecurity.AuthenticationServiceException;
022: import org.acegisecurity.BadCredentialsException;
023: import org.acegisecurity.providers.AuthenticationProvider;
024: import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
025: import org.acegisecurity.providers.encoding.PasswordEncoder;
026: import org.acegisecurity.providers.encoding.PlaintextPasswordEncoder;
027: import org.acegisecurity.userdetails.UserDetails;
028: import org.acegisecurity.userdetails.UserDetailsService;
029: import org.springframework.context.ApplicationContext;
030: import org.springframework.dao.DataAccessException;
031: import org.springframework.util.Assert;
032:
033: /**
034: * An {@link AuthenticationProvider} implementation that retrieves user details
035: * from an {@link UserDetailsService}.
036: *
037: * @author Ben Alex
038: * @version $Id: DaoAuthenticationProvider.java 1857 2007-05-24 00:47:12Z
039: * benalex $
040: */
041: public class DaoAuthenticationProvider extends
042: AbstractUserDetailsAuthenticationProvider {
043:
044: // ~ Instance fields
045: // ================================================================================================
046:
047: private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
048:
049: private SaltSource saltSource;
050:
051: private UserDetailsService userDetailsService;
052:
053: private boolean includeDetailsObject = true;
054:
055: // ~ Methods
056: // ========================================================================================================
057:
058: protected void additionalAuthenticationChecks(
059: UserDetails userDetails,
060: UsernamePasswordAuthenticationToken authentication)
061: throws AuthenticationException {
062: Object salt = null;
063:
064: if (this .saltSource != null) {
065: salt = this .saltSource.getSalt(userDetails);
066: }
067:
068: if (authentication.getCredentials() == null) {
069: throw new BadCredentialsException(
070: messages
071: .getMessage(
072: "AbstractUserDetailsAuthenticationProvider.badCredentials",
073: "Bad credentials"),
074: includeDetailsObject ? userDetails : null);
075: }
076:
077: String presentedPassword = authentication.getCredentials() == null ? ""
078: : authentication.getCredentials().toString();
079:
080: if (!passwordEncoder.isPasswordValid(userDetails.getPassword(),
081: presentedPassword, salt)) {
082: throw new BadCredentialsException(
083: messages
084: .getMessage(
085: "AbstractUserDetailsAuthenticationProvider.badCredentials",
086: "Bad credentials"),
087: includeDetailsObject ? userDetails : null);
088: }
089: }
090:
091: protected void doAfterPropertiesSet() throws Exception {
092: Assert.notNull(this .userDetailsService,
093: "A UserDetailsService must be set");
094: }
095:
096: /**
097: * Introspects the <code>Applicationcontext</code> for the single instance
098: * of {@link AccessDeniedHandler}. If found invoke
099: * setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) method by
100: * providing the found instance of accessDeniedHandler as a method
101: * parameter. If more than one instance of <code>AccessDeniedHandler</code>
102: * is found, the method throws <code>IllegalStateException</code>.
103: *
104: * @param applicationContext to locate the instance
105: */
106: private void autoDetectAnyUserDetailsServiceAndUseIt(
107: ApplicationContext applicationContext) {
108: if (applicationContext != null) {
109: Map map = applicationContext
110: .getBeansOfType(UserDetailsService.class);
111:
112: if (map.size() > 1) {
113: throw new IllegalArgumentException(
114: "More than one UserDetailsService beans detected please refer to the one using "
115: + " [ principalRepositoryBeanRef ] "
116: + "attribute");
117: } else if (map.size() == 1) {
118: setUserDetailsService((UserDetailsService) map.values()
119: .iterator().next());
120: }
121: }
122: }
123:
124: public PasswordEncoder getPasswordEncoder() {
125: return passwordEncoder;
126: }
127:
128: public SaltSource getSaltSource() {
129: return saltSource;
130: }
131:
132: public UserDetailsService getUserDetailsService() {
133: return userDetailsService;
134: }
135:
136: protected final UserDetails retrieveUser(String username,
137: UsernamePasswordAuthenticationToken authentication)
138: throws AuthenticationException {
139: UserDetails loadedUser;
140:
141: try {
142: loadedUser = this .getUserDetailsService()
143: .loadUserByUsername(username);
144: } catch (DataAccessException repositoryProblem) {
145: throw new AuthenticationServiceException(repositoryProblem
146: .getMessage(), repositoryProblem);
147: }
148:
149: if (loadedUser == null) {
150: throw new AuthenticationServiceException(
151: "UserDetailsService returned null, which is an interface contract violation");
152: }
153: return loadedUser;
154: }
155:
156: /**
157: * Sets the PasswordEncoder instance to be used to encode and validate
158: * passwords. If not set, {@link PlaintextPasswordEncoder} will be used by
159: * default.
160: *
161: * @param passwordEncoder The passwordEncoder to use
162: */
163: public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
164: this .passwordEncoder = passwordEncoder;
165: }
166:
167: /**
168: * The source of salts to use when decoding passwords. <code>null</code>
169: * is a valid value, meaning the <code>DaoAuthenticationProvider</code>
170: * will present <code>null</code> to the relevant
171: * <code>PasswordEncoder</code>.
172: *
173: * @param saltSource to use when attempting to decode passwords via the
174: * <code>PasswordEncoder</code>
175: */
176: public void setSaltSource(SaltSource saltSource) {
177: this .saltSource = saltSource;
178: }
179:
180: public void setUserDetailsService(
181: UserDetailsService userDetailsService) {
182: this .userDetailsService = userDetailsService;
183: }
184:
185: public boolean isIncludeDetailsObject() {
186: return includeDetailsObject;
187: }
188:
189: public void setIncludeDetailsObject(boolean includeDetailsObject) {
190: this.includeDetailsObject = includeDetailsObject;
191: }
192:
193: }
|