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 b4a1e910e0..36b58f1aba 100644
--- a/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java
+++ b/core/src/main/java/org/acegisecurity/providers/dao/DaoAuthenticationProvider.java
@@ -22,7 +22,8 @@ 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 net.sf.acegisecurity.providers.encoding.PasswordEncoder;
+import net.sf.acegisecurity.providers.encoding.PlaintextPasswordEncoder;
import org.springframework.beans.factory.InitializingBean;
@@ -48,6 +49,7 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
private AuthenticationDao authenticationDao;
private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
+ private SaltSource saltSource;
//~ Methods ================================================================
@@ -74,6 +76,23 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
return passwordEncoder;
}
+ /**
+ * The source of salts to use when decoding passwords. null
+ * is a valid value, meaning the DaoAuthenticationProvider
+ * will present null to the relevant
+ * PasswordEncoder.
+ *
+ * @param saltSource to use when attempting to decode passwords via the
+ * PasswordEncoder
+ */
+ public void setSaltSource(SaltSource saltSource) {
+ this.saltSource = saltSource;
+ }
+
+ public SaltSource getSaltSource() {
+ return saltSource;
+ }
+
public void afterPropertiesSet() throws Exception {
if (this.authenticationDao == null) {
throw new IllegalArgumentException(
@@ -95,8 +114,14 @@ public class DaoAuthenticationProvider implements AuthenticationProvider,
.getMessage(), repositoryProblem);
}
+ Object salt = null;
+
+ if (this.saltSource != null) {
+ salt = this.saltSource.getSalt(user);
+ }
+
if (!passwordEncoder.isPasswordValid(user.getPassword(),
- authentication.getCredentials().toString(), user)) {
+ authentication.getCredentials().toString(), salt)) {
throw new BadCredentialsException("Bad credentials presented");
}
diff --git a/core/src/main/java/org/acegisecurity/providers/dao/SaltSource.java b/core/src/main/java/org/acegisecurity/providers/dao/SaltSource.java
new file mode 100644
index 0000000000..a9ad278364
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/dao/SaltSource.java
@@ -0,0 +1,35 @@
+/* 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;
+
+/**
+ * Provides alternative sources of the salt to use for encoding passwords.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface SaltSource {
+ //~ Methods ================================================================
+
+ /**
+ * Returns the salt to use for the indicated user.
+ *
+ * @param user from the AuthenticationDao
+ *
+ * @return the salt to use for this USer
+ */
+ public Object getSalt(User user);
+}
diff --git a/core/src/main/java/org/acegisecurity/providers/dao/salt/ReflectionSaltSource.java b/core/src/main/java/org/acegisecurity/providers/dao/salt/ReflectionSaltSource.java
new file mode 100644
index 0000000000..a9a708e35b
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/dao/salt/ReflectionSaltSource.java
@@ -0,0 +1,89 @@
+/* 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.salt;
+
+import net.sf.acegisecurity.AuthenticationServiceException;
+import net.sf.acegisecurity.providers.dao.SaltSource;
+import net.sf.acegisecurity.providers.dao.User;
+
+import org.springframework.beans.factory.InitializingBean;
+
+import java.lang.reflect.Method;
+
+
+/**
+ * Obtains a salt from a specified property of the {@link User} object.
+ *
+ *
+ * This allows you to subclass User and provide an additional bean
+ * getter for a salt. You should use a synthetic value that does not change,
+ * such as a database primary key. Do not use username if it is
+ * likely to change.
+ *
User to obtain the salt.
+ *
+ *
+ * The property identified by userPropertyToUse must be
+ * available from the passed User object. If it is not
+ * available, an {@link AuthenticationServiceException} will be thrown.
+ *
userPropertyToUse
+ *
+ * @return the result of invoking user.userPropertyToUse()
+ *
+ * @throws AuthenticationServiceException if reflection fails
+ */
+ public Object getSalt(User user) {
+ try {
+ Method reflectionMethod = user.getClass().getMethod(this.userPropertyToUse,
+ null);
+
+ return reflectionMethod.invoke(user, null);
+ } catch (Exception exception) {
+ throw new AuthenticationServiceException(exception.getMessage());
+ }
+ }
+
+ public void setUserPropertyToUse(String userPropertyToUse) {
+ this.userPropertyToUse = userPropertyToUse;
+ }
+
+ public String getUserPropertyToUse() {
+ return userPropertyToUse;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ if ((this.getUserPropertyToUse() == null)
+ || "".equals(this.getUserPropertyToUse())) {
+ throw new IllegalArgumentException(
+ "A userPropertyToUse must be set");
+ }
+ }
+}
diff --git a/core/src/main/java/org/acegisecurity/providers/dao/salt/SystemWideSaltSource.java b/core/src/main/java/org/acegisecurity/providers/dao/salt/SystemWideSaltSource.java
new file mode 100644
index 0000000000..c765dc292f
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/providers/dao/salt/SystemWideSaltSource.java
@@ -0,0 +1,61 @@
+/* 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.salt;
+
+import net.sf.acegisecurity.providers.dao.SaltSource;
+import net.sf.acegisecurity.providers.dao.User;
+
+import org.springframework.beans.factory.InitializingBean;
+
+
+/**
+ * Uses a static system-wide String as the salt.
+ *
+ * + * Does not supply a different salt for each {@link User}. This means users + * sharing the same password will still have the same digested password. Of + * benefit is the digested passwords will at least be more protected than if + * stored without any salt. + *
+ * + * @author Ben Alex + * @version $Id$ + */ +public class SystemWideSaltSource implements SaltSource, InitializingBean { + //~ Instance fields ======================================================== + + private String systemWideSalt; + + //~ Methods ================================================================ + + public Object getSalt(User user) { + return this.systemWideSalt; + } + + public void setSystemWideSalt(String systemWideSalt) { + this.systemWideSalt = systemWideSalt; + } + + public String getSystemWideSalt() { + return this.systemWideSalt; + } + + public void afterPropertiesSet() throws Exception { + if ((this.systemWideSalt == null) || "".equals(this.systemWideSalt)) { + throw new IllegalArgumentException("A systemWideSalt must be set"); + } + } +} diff --git a/core/src/test/java/org/acegisecurity/providers/dao/salt/ReflectionSaltSourceTests.java b/core/src/test/java/org/acegisecurity/providers/dao/salt/ReflectionSaltSourceTests.java new file mode 100644 index 0000000000..744d4aa0c3 --- /dev/null +++ b/core/src/test/java/org/acegisecurity/providers/dao/salt/ReflectionSaltSourceTests.java @@ -0,0 +1,94 @@ +/* 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.salt; + +import junit.framework.TestCase; + +import net.sf.acegisecurity.AuthenticationServiceException; +import net.sf.acegisecurity.GrantedAuthority; +import net.sf.acegisecurity.GrantedAuthorityImpl; +import net.sf.acegisecurity.providers.dao.User; + + +/** + * Tests {@link ReflectionSaltSource}. + * + * @author Ben Alex + * @version $Id$ + */ +public class ReflectionSaltSourceTests extends TestCase { + //~ Constructors =========================================================== + + public ReflectionSaltSourceTests() { + super(); + } + + public ReflectionSaltSourceTests(String arg0) { + super(arg0); + } + + //~ Methods ================================================================ + + public final void setUp() throws Exception { + super.setUp(); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(ReflectionSaltSourceTests.class); + } + + public void testDetectsMissingUserPropertyToUse() throws Exception { + ReflectionSaltSource saltSource = new ReflectionSaltSource(); + + try { + saltSource.afterPropertiesSet(); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + assertEquals("A userPropertyToUse must be set", + expected.getMessage()); + } + } + + public void testExceptionWhenInvalidPropertyRequested() { + ReflectionSaltSource saltSource = new ReflectionSaltSource(); + saltSource.setUserPropertyToUse("getDoesNotExist"); + + User user = new User("scott", "wombat", true, + new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")}); + + try { + saltSource.getSalt(user); + fail("Should have thrown AuthenticationServiceException"); + } catch (AuthenticationServiceException expected) { + assertTrue(true); + } + } + + public void testGettersSetters() { + ReflectionSaltSource saltSource = new ReflectionSaltSource(); + saltSource.setUserPropertyToUse("getUsername"); + assertEquals("getUsername", saltSource.getUserPropertyToUse()); + } + + public void testNormalOperation() { + ReflectionSaltSource saltSource = new ReflectionSaltSource(); + saltSource.setUserPropertyToUse("getUsername"); + + User user = new User("scott", "wombat", true, + new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")}); + assertEquals("scott", saltSource.getSalt(user)); + } +} diff --git a/core/src/test/java/org/acegisecurity/providers/dao/salt/SystemWideSaltSourceTests.java b/core/src/test/java/org/acegisecurity/providers/dao/salt/SystemWideSaltSourceTests.java new file mode 100644 index 0000000000..9840202055 --- /dev/null +++ b/core/src/test/java/org/acegisecurity/providers/dao/salt/SystemWideSaltSourceTests.java @@ -0,0 +1,70 @@ +/* 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.salt; + +import junit.framework.TestCase; + + +/** + * Tests {@link SystemWideSaltSource}. + * + * @author Ben Alex + * @version $Id$ + */ +public class SystemWideSaltSourceTests extends TestCase { + //~ Constructors =========================================================== + + public SystemWideSaltSourceTests() { + super(); + } + + public SystemWideSaltSourceTests(String arg0) { + super(arg0); + } + + //~ Methods ================================================================ + + public final void setUp() throws Exception { + super.setUp(); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(SystemWideSaltSourceTests.class); + } + + public void testDetectsMissingSystemWideSalt() throws Exception { + SystemWideSaltSource saltSource = new SystemWideSaltSource(); + + try { + saltSource.afterPropertiesSet(); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + assertEquals("A systemWideSalt must be set", expected.getMessage()); + } + } + + public void testGettersSetters() { + SystemWideSaltSource saltSource = new SystemWideSaltSource(); + saltSource.setSystemWideSalt("helloWorld"); + assertEquals("helloWorld", saltSource.getSystemWideSalt()); + } + + public void testNormalOperation() { + SystemWideSaltSource saltSource = new SystemWideSaltSource(); + saltSource.setSystemWideSalt("helloWorld"); + assertEquals("helloWorld", saltSource.getSalt(null)); + } +}