From aed9d2a1d88f0aad44bd0bea32dc5ac5b1021627 Mon Sep 17 00:00:00 2001 From: Colin Sampaleanu Date: Wed, 14 Apr 2004 21:30:59 +0000 Subject: [PATCH] initial cut at allowing pluggable digest strategy for use in password handling in DaoAuthenticationProvider --- .../dao/DaoAuthenticationProvider.java | 30 +++++--- .../providers/dao/MD5PasswordEncoder.java | 51 +++++++++++++ .../providers/dao/PasswordEncoder.java | 38 ++++++++++ .../dao/PlaintextPasswordEncoder.java | 50 +++++++++++++ .../providers/dao/SHAPasswordEncoder.java | 51 +++++++++++++ .../userdetails/jdbc/JdbcDaoImpl.java | 75 +++++++++++-------- 6 files changed, 253 insertions(+), 42 deletions(-) create mode 100644 core/src/main/java/org/acegisecurity/providers/dao/MD5PasswordEncoder.java create mode 100644 core/src/main/java/org/acegisecurity/providers/dao/PasswordEncoder.java create mode 100644 core/src/main/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoder.java create mode 100644 core/src/main/java/org/acegisecurity/providers/dao/SHAPasswordEncoder.java diff --git a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java index cd1249084b..0f06d285f3 100644 --- a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java +++ b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java @@ -46,6 +46,7 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, //~ Instance fields ======================================================== private AuthenticationDao authenticationDao; + private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder(); private boolean ignorePasswordCase = false; private boolean ignoreUsernameCase = true; @@ -89,6 +90,21 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, return ignoreUsernameCase; } + /** + * Sets the PasswordEncoder instance to be used to encode and validate + * passwords. If not set, {@link PlaintextPasswordEncoder} will be used by + * default. + * + * @param passwordEncoder The passwordEncoder to use + */ + public void setPasswordEncoder(PasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + } + + public PasswordEncoder getPasswordEncoder() { + return passwordEncoder; + } + public void afterPropertiesSet() throws Exception { if (this.authenticationDao == null) { throw new IllegalArgumentException( @@ -116,15 +132,9 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, throw new BadCredentialsException("Bad credentials presented"); } - if (!user.getPassword().toLowerCase().equals(authentication.getCredentials() - .toString() - .toLowerCase())) { - throw new BadCredentialsException("Bad credentials presented"); - } - - if ((!this.ignorePasswordCase) - && (!user.getPassword().equals(authentication.getCredentials() - .toString()))) { + if (!passwordEncoder.isPasswordValid(user.getPassword(), + authentication.getCredentials().toString(), user, + ignorePasswordCase)) { throw new BadCredentialsException("Bad credentials presented"); } @@ -133,7 +143,7 @@ public class DaoAuthenticationProvider implements AuthenticationProvider, } return new UsernamePasswordAuthenticationToken(user.getUsername(), - user.getPassword(), user.getAuthorities()); + authentication.getCredentials().toString(), user.getAuthorities()); } public boolean supports(Class authentication) { diff --git a/core/src/main/java/org/acegisecurity/providers/dao/MD5PasswordEncoder.java b/core/src/main/java/org/acegisecurity/providers/dao/MD5PasswordEncoder.java new file mode 100644 index 0000000000..e51f359efd --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/dao/MD5PasswordEncoder.java @@ -0,0 +1,51 @@ +/* Copyright 2004 Acegi Technology Pty Limited + * + * 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 net.sf.acegisecurity.providers.dao; + +import org.apache.commons.codec.digest.DigestUtils; + + +/** + *

+ * MD5 implementation of PasswordEncoder.
The ignorePasswordCase parameter is not used for this implementation.
A null password is encoded to the same value as an empty ("") password. + *

+ * + * @author colin sampaleanu + * @version $Id$ + */ +public class MD5PasswordEncoder implements PasswordEncoder { + //~ Methods ================================================================ + + /* (non-Javadoc) + * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(net.sf.acegisecurity.providers.dao.User, java.lang.String, boolean) + */ + public boolean isPasswordValid(String encPass, String rawPass, + Object saltSource, boolean ignorePasswordCase) { + String pass1 = "" + encPass; + String pass2 = DigestUtils.md5Hex("" + rawPass); + + return pass1.equals(pass2); + } + + /* (non-Javadoc) + * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#encodePassword(java.lang.String, java.lang.Object) + */ + public String encodePassword(String rawPass, Object saltSource) { + return DigestUtils.md5Hex("" + rawPass); + } +} diff --git a/core/src/main/java/org/acegisecurity/providers/dao/PasswordEncoder.java b/core/src/main/java/org/acegisecurity/providers/dao/PasswordEncoder.java new file mode 100644 index 0000000000..c7a91340ad --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/dao/PasswordEncoder.java @@ -0,0 +1,38 @@ +/* Copyright 2004 Acegi Technology Pty Limited + * + * 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 net.sf.acegisecurity.providers.dao; + +import org.springframework.dao.DataAccessException; + + +/** + *

+ * Describes authentication operations on a password, so that digest algorithms + * can be abstracted + *

+ * + * @author colin sampaleanu + * @version $Id$ + */ +public interface PasswordEncoder { + //~ Methods ================================================================ + + public boolean isPasswordValid(String encPass, String rawPass, + Object saltSource, boolean ignorePasswordCase) + throws DataAccessException; + + public String encodePassword(String rawPass, Object saltSource); +} diff --git a/core/src/main/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoder.java b/core/src/main/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoder.java new file mode 100644 index 0000000000..e289740732 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoder.java @@ -0,0 +1,50 @@ +/* Copyright 2004 Acegi Technology Pty Limited + * + * 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 net.sf.acegisecurity.providers.dao; + +/** + *

+ * Plaintext implementation of PasswordEncoder. + *

+ * + * @author colin sampaleanu + * @version $Id$ + */ +public class PlaintextPasswordEncoder implements PasswordEncoder { + //~ Methods ================================================================ + + /* (non-Javadoc) + * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(net.sf.acegisecurity.providers.dao.User, java.lang.String, boolean) + */ + public boolean isPasswordValid(String encPass, String rawPass, + Object saltSource, boolean ignorePasswordCase) { + String pass1 = "" + encPass; + String pass2 = "" + rawPass; + + if (!ignorePasswordCase) { + return pass1.equals(pass2); + } else { + return pass1.equalsIgnoreCase(pass2); + } + } + + /* (non-Javadoc) + * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#encodePassword(java.lang.String, java.lang.Object) + */ + public String encodePassword(String rawPass, Object saltSource) { + return rawPass; + } +} diff --git a/core/src/main/java/org/acegisecurity/providers/dao/SHAPasswordEncoder.java b/core/src/main/java/org/acegisecurity/providers/dao/SHAPasswordEncoder.java new file mode 100644 index 0000000000..9b830b0904 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/providers/dao/SHAPasswordEncoder.java @@ -0,0 +1,51 @@ +/* Copyright 2004 Acegi Technology Pty Limited + * + * 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 net.sf.acegisecurity.providers.dao; + +import org.apache.commons.codec.digest.DigestUtils; + + +/** + *

+ * SHA implementation of PasswordEncoder.
The ignorePasswordCase parameter is not used for this implementation.
A null password is encoded to the same value as an empty ("") password. + *

+ * + * @author colin sampaleanu + * @version $Id$ + */ +public class SHAPasswordEncoder implements PasswordEncoder { + //~ Methods ================================================================ + + /* (non-Javadoc) + * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(net.sf.acegisecurity.providers.dao.User, java.lang.String, boolean) + */ + public boolean isPasswordValid(String encPass, String rawPass, + Object saltSource, boolean ignorePasswordCase) { + String pass1 = "" + encPass; + String pass2 = DigestUtils.shaHex("" + rawPass); + + return pass1.equals(pass2); + } + + /* (non-Javadoc) + * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#encodePassword(java.lang.String, java.lang.Object) + */ + public String encodePassword(String rawPass, Object saltSource) { + return DigestUtils.shaHex("" + rawPass); + } +} diff --git a/core/src/main/java/org/acegisecurity/userdetails/jdbc/JdbcDaoImpl.java b/core/src/main/java/org/acegisecurity/userdetails/jdbc/JdbcDaoImpl.java index 7702b95d4d..922980296c 100644 --- a/core/src/main/java/org/acegisecurity/userdetails/jdbc/JdbcDaoImpl.java +++ b/core/src/main/java/org/acegisecurity/userdetails/jdbc/JdbcDaoImpl.java @@ -48,11 +48,14 @@ import javax.sql.DataSource; *

* *

- * A default database structure is assumed, which most users of this class will + * A default database structure is assumed, (see {@link + * #DEF_USERS_BY_USERNAME_QUERY} and {@link + * #DEF_AUTHORITIES_BY_USERNAME_QUERY}, which most users of this class will * need to override, if using an existing scheme. This may be done by setting - * the default query strings used, or if that does not provide enough - * flexibility, setting the actual {@link MappingSqlQuery} instances used to - * query the database. + * the default query strings used. If this does not provide enough + * flexibility, another strategy would be to subclass this class and override + * the {@link MappingSqlQuery} instances used, via the {@link + * #initMappingSqlQueries()} extension point. *

* * @author Ben Alex @@ -68,10 +71,11 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao { //~ Instance fields ======================================================== - protected MappingSqlQuery authoritiesByUsernameMapping; - protected MappingSqlQuery usersByUsernameMapping; - protected String authoritiesByUsernameQuery; - protected String usersByUsernameQuery; + private MappingSqlQuery authoritiesByUsernameMapping; + private MappingSqlQuery usersByUsernameMapping; + private String authoritiesByUsernameQuery; + private String rolePrefix = "ROLE_"; + private String usersByUsernameQuery; //~ Constructors =========================================================== @@ -82,16 +86,6 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao { //~ Methods ================================================================ - /** - * Allows the default MappingSqlQuery used for retrieving authorities by - * username to be overriden. This may be used when overriding the query - * string alone is inadequate. Note that there is no point in setting this - * property and also specifying a query string, since there will be no - * way for the instance set by this property to get at the query string. - * As such, the MappingSqlQuery should be self contained. - * - * @param authoritiesByUsernameQuery The authoritiesByUsernameQuery to set. - */ public void setAuthoritiesByUsernameMapping( MappingSqlQuery authoritiesByUsernameQuery) { this.authoritiesByUsernameMapping = authoritiesByUsernameQuery; @@ -120,15 +114,23 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao { } /** - * Allows the default MappingSqlQuery used for retrieving users by username - * to be overriden. This may be used when overriding the query string - * alone is inadequate. Note that there is no point in setting this - * property and also specifying a query string, since there will be no - * way for the instance set by this property to get at the query string. - * As such, the MappingSqlQuery should be self contained. + * Allows a default role prefix to be specified. If this is set to a + * non-empty value, then it is automatically prepended to any roles read + * in from the db. This may for example be used to add the + * ROLE_ prefix expected to exist in role names (by default) + * by some other Acegi Security framework classes, in the case that the + * prefix is not already present in the db. * - * @param usersByUsernameQuery The MappingSqlQuery to set. + * @param rolePrefix the new prefix */ + public void setRolePrefix(String rolePrefix) { + this.rolePrefix = rolePrefix; + } + + public String getRolePrefix() { + return rolePrefix; + } + public void setUsersByUsernameMapping(MappingSqlQuery usersByUsernameQuery) { this.usersByUsernameMapping = usersByUsernameQuery; } @@ -183,8 +185,17 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao { } protected void initDao() throws ApplicationContextException { - usersByUsernameMapping = new UsersByUsernameMapping(getDataSource()); - authoritiesByUsernameMapping = new AuthoritiesByUsernameMapping(getDataSource()); + initMappingSqlQueries(); + } + + /** + * Extension point to allow other MappingSqlQuery objects to be substituted + * in a subclass + */ + protected void initMappingSqlQueries() { + setUsersByUsernameMapping(new UsersByUsernameMapping(getDataSource())); + setAuthoritiesByUsernameMapping(new AuthoritiesByUsernameMapping( + getDataSource())); } //~ Inner Classes ========================================================== @@ -201,8 +212,8 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao { protected Object mapRow(ResultSet rs, int rownum) throws SQLException { - GrantedAuthorityImpl authority = new GrantedAuthorityImpl(rs - .getString("authority")); + String roleName = rolePrefix + rs.getString(1); + GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName); return authority; } @@ -220,9 +231,9 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao { protected Object mapRow(ResultSet rs, int rownum) throws SQLException { - String username = rs.getString("username"); - String password = rs.getString("password"); - boolean enabled = rs.getBoolean("enabled"); + String username = rs.getString(1); + String password = rs.getString(2); + boolean enabled = rs.getBoolean(3); User user = new User(username, password, enabled, new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")});