Browse Source

initial cut at allowing pluggable digest strategy for use in password handling in DaoAuthenticationProvider

Colin Sampaleanu 21 years ago
parent
commit
aed9d2a1d8

+ 20 - 10
core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java

@@ -46,6 +46,7 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
     //~ Instance fields ========================================================
     //~ Instance fields ========================================================
 
 
     private AuthenticationDao authenticationDao;
     private AuthenticationDao authenticationDao;
+    private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
     private boolean ignorePasswordCase = false;
     private boolean ignorePasswordCase = false;
     private boolean ignoreUsernameCase = true;
     private boolean ignoreUsernameCase = true;
 
 
@@ -89,6 +90,21 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
         return ignoreUsernameCase;
         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 {
     public void afterPropertiesSet() throws Exception {
         if (this.authenticationDao == null) {
         if (this.authenticationDao == null) {
             throw new IllegalArgumentException(
             throw new IllegalArgumentException(
@@ -116,15 +132,9 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
             throw new BadCredentialsException("Bad credentials presented");
             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");
             throw new BadCredentialsException("Bad credentials presented");
         }
         }
 
 
@@ -133,7 +143,7 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
         }
         }
 
 
         return new UsernamePasswordAuthenticationToken(user.getUsername(),
         return new UsernamePasswordAuthenticationToken(user.getUsername(),
-            user.getPassword(), user.getAuthorities());
+            authentication.getCredentials().toString(), user.getAuthorities());
     }
     }
 
 
     public boolean supports(Class authentication) {
     public boolean supports(Class authentication) {

+ 51 - 0
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;
+
+
+/**
+ * <p>
+ * MD5 implementation of PasswordEncoder.<br/
+ * > The ignorePasswordCase parameter is not used for this implementation.<br/
+ * > A null password is encoded to the same value as an empty ("") password.
+ * </p>
+ *
+ * @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);
+    }
+}

+ 38 - 0
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;
+
+
+/**
+ * <p>
+ * Describes authentication operations on a password, so that digest algorithms
+ * can be abstracted
+ * </p>
+ *
+ * @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);
+}

+ 50 - 0
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;
+
+/**
+ * <p>
+ * Plaintext implementation of PasswordEncoder.
+ * </p>
+ *
+ * @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;
+    }
+}

+ 51 - 0
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;
+
+
+/**
+ * <p>
+ * SHA implementation of PasswordEncoder.<br/
+ * > The ignorePasswordCase parameter is not used for this implementation.<br/
+ * > A null password is encoded to the same value as an empty ("") password.
+ * </p>
+ *
+ * @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);
+    }
+}

+ 43 - 32
core/src/main/java/org/acegisecurity/userdetails/jdbc/JdbcDaoImpl.java

@@ -48,11 +48,14 @@ import javax.sql.DataSource;
  * </p>
  * </p>
  * 
  * 
  * <p>
  * <p>
- * 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
  * 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.
  * </p>
  * </p>
  *
  *
  * @author Ben Alex
  * @author Ben Alex
@@ -68,10 +71,11 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao {
 
 
     //~ Instance fields ========================================================
     //~ 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 ===========================================================
     //~ Constructors ===========================================================
 
 
@@ -82,16 +86,6 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao {
 
 
     //~ Methods ================================================================
     //~ 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(
     public void setAuthoritiesByUsernameMapping(
         MappingSqlQuery authoritiesByUsernameQuery) {
         MappingSqlQuery authoritiesByUsernameQuery) {
         this.authoritiesByUsernameMapping = 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
+     * <code>ROLE_</code> 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) {
     public void setUsersByUsernameMapping(MappingSqlQuery usersByUsernameQuery) {
         this.usersByUsernameMapping = usersByUsernameQuery;
         this.usersByUsernameMapping = usersByUsernameQuery;
     }
     }
@@ -183,8 +185,17 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao {
     }
     }
 
 
     protected void initDao() throws ApplicationContextException {
     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 ==========================================================
     //~ Inner Classes ==========================================================
@@ -201,8 +212,8 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao {
 
 
         protected Object mapRow(ResultSet rs, int rownum)
         protected Object mapRow(ResultSet rs, int rownum)
             throws SQLException {
             throws SQLException {
-            GrantedAuthorityImpl authority = new GrantedAuthorityImpl(rs
-                    .getString("authority"));
+            String roleName = rolePrefix + rs.getString(1);
+            GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName);
 
 
             return authority;
             return authority;
         }
         }
@@ -220,9 +231,9 @@ public class JdbcDaoImpl extends JdbcDaoSupport implements AuthenticationDao {
 
 
         protected Object mapRow(ResultSet rs, int rownum)
         protected Object mapRow(ResultSet rs, int rownum)
             throws SQLException {
             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,
             User user = new User(username, password, enabled,
                     new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")});
                     new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")});