Pārlūkot izejas kodu

move and rename password encoding classes.
change saltSource arument to salt argument, which impl may or may not use.

Colin Sampaleanu 21 gadi atpakaļ
vecāks
revīzija
3d089aaa67

+ 3 - 1
core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java

@@ -22,6 +22,7 @@ import net.sf.acegisecurity.BadCredentialsException;
 import net.sf.acegisecurity.DisabledException;
 import net.sf.acegisecurity.providers.AuthenticationProvider;
 import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+import net.sf.acegisecurity.providers.encoding.*;
 
 import org.springframework.beans.factory.InitializingBean;
 
@@ -95,8 +96,9 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
         }
 
         if (!passwordEncoder.isPasswordValid(user.getPassword(),
-                authentication.getCredentials().toString(), user))
+                authentication.getCredentials().toString(), user)) {
             throw new BadCredentialsException("Bad credentials presented");
+        }
 
         if (!user.isEnabled()) {
             throw new DisabledException("User is disabled");

+ 50 - 0
core/src/main/java/org/acegisecurity/providers/encoding/BaseDigestPasswordEncoder.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.encoding;
+
+import net.sf.acegisecurity.providers.encoding.*;
+
+
+/**
+ * <p>
+ * Convenience base for Digest password encoders
+ * </p>
+ *
+ * @author colin sampaleanu
+ * @version $Id$
+ */
+public abstract class BaseDigestPasswordEncoder implements PasswordEncoder {
+    //~ Instance fields ========================================================
+
+    private boolean encodeHashAsBase64 = false;
+
+    //~ Methods ================================================================
+
+    /**
+     * The encoded password is normally returned as Hex (32 char) version of
+     * the hash bytes. Setting this property to true will cause the encoded
+     * pass to be returned as Base64 text, which will consume 24 characters.
+     *
+     * @param encodeHashAsBase64 DOCUMENT ME!
+     */
+    public void setEncodeHashAsBase64(boolean encodeHashAsBase64) {
+        this.encodeHashAsBase64 = encodeHashAsBase64;
+    }
+
+    public boolean getEncodeHashAsBase64() {
+        return encodeHashAsBase64;
+    }
+}

+ 64 - 0
core/src/main/java/org/acegisecurity/providers/encoding/Md5PasswordEncoder.java

@@ -0,0 +1,64 @@
+/* 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.encoding;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.DigestUtils;
+
+
+/**
+ * <p>
+ * MD5 implementation of PasswordEncoder.
+ * </p>
+ * 
+ * <p>
+ * A null password is encoded to the same value as an empty ("") password.
+ * </p>
+ *
+ * @author colin sampaleanu
+ * @version $Id$
+ */
+public class Md5PasswordEncoder extends BaseDigestPasswordEncoder
+    implements PasswordEncoder {
+    //~ Methods ================================================================
+
+    /* (non-Javadoc)
+     * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(java.lang.String, java.lang.String, java.lang.Object)
+     */
+    public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
+        String pass1 = "" + encPass;
+        String pass2 = encodeInternal("" + 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 salt) {
+        return encodeInternal("" + rawPass);
+    }
+
+    private String encodeInternal(String input) {
+        if (!getEncodeHashAsBase64()) {
+            return DigestUtils.md5Hex(input);
+        }
+
+        byte[] encoded = Base64.encodeBase64(DigestUtils.md5(input));
+
+        return new String(encoded);
+    }
+}

+ 87 - 0
core/src/main/java/org/acegisecurity/providers/encoding/PasswordEncoder.java

@@ -0,0 +1,87 @@
+/* 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.encoding;
+
+import org.springframework.dao.DataAccessException;
+
+
+/**
+ * <p>
+ * Interface for performing authentication operations on a password, so that
+ * digest algorithms may be abstracted.
+ * </p>
+ *
+ * @author colin sampaleanu
+ * @version $Id$
+ */
+public interface PasswordEncoder {
+    //~ Methods ================================================================
+
+    /**
+     * <p>
+     * Validates a specified 'raw' password against an encoded password
+     * previously returned form {@link #encodePassword(String, Object)}. The
+     * raw password will first be encoded, and then both values will be
+     * compared.
+     * </p>
+     * 
+     * <p>
+     * The specified salt will potentially be used by the implementation to
+     * 'salt' the initial value before encoding. If a salt value is provided,
+     * it must be the same as the value used when calling {@link
+     * #encodePassword(String, Object)} to produce the first encoded value.
+     * Note that a specific implementation may choose to ignore the salt
+     * value, or provide its own.
+     * </p>
+     *
+     * @param encPass a pre-encoded password
+     * @param rawPass a raw password to encode and compare against the
+     *        pre-encoded password
+     * @param an object optionally used by the implementation to 'salt' the raw
+     *        password before encoding. A null value is legal.
+     *
+     * @return DOCUMENT ME!
+     */
+    public boolean isPasswordValid(String encPass, String rawPass,
+        Object saltSource) throws DataAccessException;
+
+    /**
+     * <p>
+     * Encodes the specified raw password with an implementation specific
+     * algorithm. This will generally be a one-way message digest such as MD5
+     * or SHA, but may also be a plaintext variant which does no encoding at
+     * all, but rather returns the same password it was fed. The latter is
+     * useful to plug in when the original password must be stored as-is.
+     * </p>
+     * 
+     * <p>
+     * The specified salt will potentially be used by the implementation to
+     * 'salt' the initial value before encoding, in order to prevent
+     * dictionary attacks. If a salt value is provided, the same salt value
+     * must be use when calling the  {@link #isPasswordValid(String, String,
+     * Object)} function. Note that a specific implementation may choose to
+     * ignore the salt value, or provide its own.
+     * </p>
+     *
+     * @param rawPass the password to encode
+     * @param an object optionally used by the implementation to 'salt' the raw
+     *        password before encoding. A null value is legal.
+     *
+     * @return DOCUMENT ME!
+     */
+    public String encodePassword(String rawPass, Object salt)
+        throws DataAccessException;
+}

+ 68 - 0
core/src/main/java/org/acegisecurity/providers/encoding/PlaintextPasswordEncoder.java

@@ -0,0 +1,68 @@
+/* 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.encoding;
+
+/**
+ * <p>
+ * Plaintext implementation of PasswordEncoder.
+ * </p>
+ *
+ * @author colin sampaleanu
+ * @version $Id$
+ */
+public class PlaintextPasswordEncoder implements PasswordEncoder {
+    //~ Instance fields ========================================================
+
+    private boolean ignorePasswordCase = false;
+
+    //~ Methods ================================================================
+
+    /**
+     * Indicates whether the password comparison is case sensitive. Defaults to
+     * <code>false</code>, meaning an exact case match is required.
+     *
+     * @param ignorePasswordCase set to <code>true</code> for less stringent
+     *        comparison
+     */
+    public void setIgnorePasswordCase(boolean ignorePasswordCase) {
+        this.ignorePasswordCase = ignorePasswordCase;
+    }
+
+    public boolean isIgnorePasswordCase() {
+        return ignorePasswordCase;
+    }
+
+    /* (non-Javadoc)
+     * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(java.lang.String, java.lang.String, java.lang.Object)
+     */
+    public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
+        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 salt) {
+        return rawPass;
+    }
+}

+ 64 - 0
core/src/main/java/org/acegisecurity/providers/encoding/ShaPasswordEncoder.java

@@ -0,0 +1,64 @@
+/* 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.encoding;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.DigestUtils;
+
+
+/**
+ * <p>
+ * SHA implementation of PasswordEncoder.
+ * </p>
+ * 
+ * <p>
+ * A null password is encoded to the same value as an empty ("") password.
+ * </p>
+ *
+ * @author colin sampaleanu
+ * @version $Id$
+ */
+public class ShaPasswordEncoder extends BaseDigestPasswordEncoder
+    implements PasswordEncoder {
+    //~ Methods ================================================================
+
+    /* (non-Javadoc)
+     * @see net.sf.acegisecurity.providers.dao.PasswordEncoder#isPasswordValid(java.lang.String, java.lang.String, java.lang.Object)
+     */
+    public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
+        String pass1 = "" + encPass;
+        String pass2 = encodeInternal("" + 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 salt) {
+        return encodeInternal("" + rawPass);
+    }
+
+    private String encodeInternal(String input) {
+        if (!getEncodeHashAsBase64()) {
+            return DigestUtils.shaHex(input);
+        }
+
+        byte[] encoded = Base64.encodeBase64(DigestUtils.sha(input));
+
+        return new String(encoded);
+    }
+}

+ 3 - 3
core/src/main/java/org/acegisecurity/userdetails/UserDetailsService.java

@@ -36,9 +36,9 @@ public interface AuthenticationDao {
     /**
      * Locates the user based on the username. In the actual implementation,
      * the search may possibly be case insensitive, or case insensitive
-     * depending on how the implementaion instance is configured. In this case,
-     * the User object that comes back may have a username that is of a different
-     * case than what was actually requested..
+     * depending on how the implementaion instance is configured. In this
+     * case, the User object that comes back may have a username that is of a
+     * different case than what was actually requested..
      *
      * @param username the username presented to the {@link
      *        DaoAuthenticationProvider}

+ 0 - 1
core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java

@@ -37,7 +37,6 @@ import org.springframework.dao.DataRetrievalFailureException;
  * @version $Id$
  */
 public class DaoAuthenticationProviderTests extends TestCase {
-
     //~ Methods ================================================================
 
     public final void setUp() throws Exception {

+ 20 - 17
core/src/test/java/org/acegisecurity/providers/dao/MD5PasswordEncoderTest.java

@@ -12,10 +12,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package net.sf.acegisecurity.providers.dao;
 
 import junit.framework.TestCase;
 
+import net.sf.acegisecurity.providers.encoding.*;
+
+
 /**
  * <p>
  * TestCase for PlaintextPasswordEncoder.
@@ -25,23 +29,22 @@ import junit.framework.TestCase;
  * @version $Id$
  */
 public class MD5PasswordEncoderTest extends TestCase {
+    //~ Methods ================================================================
 
-  public void testBasicFunctionality() {
-    
-    MD5PasswordEncoder pe = new MD5PasswordEncoder();
-    String raw = "abc123";
-    String badRaw = "abc321";
-    String encoded = pe.encodePassword(raw, null);   // no SALT source
-    assertTrue(pe.isPasswordValid(encoded, raw, null));
-    assertFalse(pe.isPasswordValid(encoded, badRaw, null));
-    assertTrue(encoded.length() == 32);
-    
-    // now try Base64
-    pe.setEncodeHashAsBase64(true);
-    encoded = pe.encodePassword(raw, null);   // no SALT source
-    assertTrue(pe.isPasswordValid(encoded, raw, null));
-    assertFalse(pe.isPasswordValid(encoded, badRaw, null));
-    assertTrue(encoded.length() != 32);
-  }
+    public void testBasicFunctionality() {
+        Md5PasswordEncoder pe = new Md5PasswordEncoder();
+        String raw = "abc123";
+        String badRaw = "abc321";
+        String encoded = pe.encodePassword(raw, null); // no SALT source
+        assertTrue(pe.isPasswordValid(encoded, raw, null));
+        assertFalse(pe.isPasswordValid(encoded, badRaw, null));
+        assertTrue(encoded.length() == 32);
 
+        // now try Base64
+        pe.setEncodeHashAsBase64(true);
+        encoded = pe.encodePassword(raw, null); // no SALT source
+        assertTrue(pe.isPasswordValid(encoded, raw, null));
+        assertFalse(pe.isPasswordValid(encoded, badRaw, null));
+        assertTrue(encoded.length() != 32);
+    }
 }

+ 36 - 31
core/src/test/java/org/acegisecurity/providers/dao/PlaintextPasswordEncoderTest.java

@@ -12,10 +12,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package net.sf.acegisecurity.providers.dao;
 
 import junit.framework.TestCase;
 
+import net.sf.acegisecurity.providers.encoding.*;
+
+
 /**
  * <p>
  * TestCase for PlaintextPasswordEncoder.
@@ -25,35 +29,36 @@ import junit.framework.TestCase;
  * @version $Id$
  */
 public class PlaintextPasswordEncoderTest extends TestCase {
+    //~ Methods ================================================================
+
+    public void testBasicFunctionality() {
+        PlaintextPasswordEncoder pe = new PlaintextPasswordEncoder();
+
+        String raw = "abc123";
+        String rawDiffCase = "AbC123";
+        String badRaw = "abc321";
+
+        // should be able to validate even without encoding
+        String encoded = raw;
+        assertTrue(pe.isPasswordValid(encoded, raw, null)); // no SALT source
+        assertFalse(pe.isPasswordValid(encoded, badRaw, null));
+
+        // now make sure encoded version it gives us back is comparable as well
+        encoded = pe.encodePassword(raw, null);
+        assertTrue(pe.isPasswordValid(encoded, raw, null)); // no SALT source
+        assertFalse(pe.isPasswordValid(encoded, badRaw, null));
+
+        // make sure default is not to ignore password case
+        encoded = pe.encodePassword(rawDiffCase, null);
+        assertFalse(pe.isPasswordValid(encoded, raw, null));
+
+        // now check for ignore password case
+        pe = new PlaintextPasswordEncoder();
+        pe.setIgnorePasswordCase(true);
 
-  public void testBasicFunctionality() {
-    PlaintextPasswordEncoder pe = new PlaintextPasswordEncoder();
-
-    String raw = "abc123";
-    String rawDiffCase = "AbC123";
-    String badRaw = "abc321";
-    // should be able to validate even without encoding
-    String encoded = raw;
-    assertTrue(pe.isPasswordValid(encoded, raw, null));   // no SALT source
-    assertFalse(pe.isPasswordValid(encoded, badRaw, null));
-
-    // now make sure encoded version it gives us back is comparable as well
-    encoded = pe.encodePassword(raw, null);
-    assertTrue(pe.isPasswordValid(encoded, raw, null));   // no SALT source
-    assertFalse(pe.isPasswordValid(encoded, badRaw, null));
-    
-    // make sure default is not to ignore password case
-    encoded = pe.encodePassword(rawDiffCase, null);
-    assertFalse(pe.isPasswordValid(encoded, raw, null));
-    
-    // now check for ignore password case
-    pe = new PlaintextPasswordEncoder();
-    pe.setIgnorePasswordCase(true);
-
-    // should be able to validate even without encoding
-    encoded = pe.encodePassword(rawDiffCase, null);
-    assertTrue(pe.isPasswordValid(encoded, raw, null));
-    assertFalse(pe.isPasswordValid(encoded, badRaw, null));
-  }
-
-}
+        // should be able to validate even without encoding
+        encoded = pe.encodePassword(rawDiffCase, null);
+        assertTrue(pe.isPasswordValid(encoded, raw, null));
+        assertFalse(pe.isPasswordValid(encoded, badRaw, null));
+    }
+}

+ 22 - 19
core/src/test/java/org/acegisecurity/providers/dao/SHAPasswordEncoderTest.java

@@ -12,36 +12,39 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package net.sf.acegisecurity.providers.dao;
 
 import junit.framework.TestCase;
 
+import net.sf.acegisecurity.providers.encoding.*;
+
+
 /**
  * <p>
- * TestCase for SHAPasswordEncoder.
+ * TestCase for ShaPasswordEncoder.
  * </p>
  *
  * @author colin sampaleanu
  * @version $Id$
  */
 public class SHAPasswordEncoderTest extends TestCase {
+    //~ Methods ================================================================
+
+    public void testBasicFunctionality() {
+        ShaPasswordEncoder pe = new ShaPasswordEncoder();
+        String raw = "abc123";
+        String badRaw = "abc321";
+        String encoded = pe.encodePassword(raw, null); // no SALT source
+        assertTrue(pe.isPasswordValid(encoded, raw, null));
+        assertFalse(pe.isPasswordValid(encoded, badRaw, null));
+        assertTrue(encoded.length() == 40);
 
-  public void testBasicFunctionality() {
-    
-    SHAPasswordEncoder pe = new SHAPasswordEncoder();
-    String raw = "abc123";
-    String badRaw = "abc321";
-    String encoded = pe.encodePassword(raw, null);   // no SALT source
-    assertTrue(pe.isPasswordValid(encoded, raw, null));
-    assertFalse(pe.isPasswordValid(encoded, badRaw, null));
-    assertTrue(encoded.length() == 40);
-    
-    // now try Base64
-    pe.setEncodeHashAsBase64(true);
-    encoded = pe.encodePassword(raw, null);   // no SALT source
-    assertTrue(pe.isPasswordValid(encoded, raw, null));
-    assertFalse(pe.isPasswordValid(encoded, badRaw, null));
-    assertTrue(encoded.length() != 40);
-    
-  }
+        // now try Base64
+        pe.setEncodeHashAsBase64(true);
+        encoded = pe.encodePassword(raw, null); // no SALT source
+        assertTrue(pe.isPasswordValid(encoded, raw, null));
+        assertFalse(pe.isPasswordValid(encoded, badRaw, null));
+        assertTrue(encoded.length() != 40);
+    }
 }

+ 1 - 1
samples/contacts/src/main/java/sample/contact/ClientApplication.java

@@ -30,7 +30,7 @@ import java.util.Map;
 
 /**
  * Demonstrates accessing the {@link ContactManager} via remoting protocols.
- * 
+ *
  * <P>
  * Based on Spring's JPetStore sample, written by Juergen Hoeller.
  * </p>

+ 1 - 1
test/acegisecuritytest.properties

@@ -1,5 +1,5 @@
 #HSQL database
-#Thu Apr 15 12:20:59 EDT 2004
+#Thu Apr 15 23:43:47 EDT 2004
 sql.strict_fk=true
 readonly=false
 sql.strong_fk=true