diff --git a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java index 9855595cb5..cda21593e7 100644 --- a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2014 Alfresco Software Limited. + * Copyright (C) 2005-2015 Alfresco Software Limited. * * This file is part of Alfresco * @@ -19,7 +19,6 @@ package org.alfresco.repo.security.authentication; import java.io.Serializable; -import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; @@ -29,7 +28,6 @@ import java.util.Map; import net.sf.acegisecurity.GrantedAuthority; import net.sf.acegisecurity.GrantedAuthorityImpl; import net.sf.acegisecurity.UserDetails; -import net.sf.acegisecurity.providers.dao.User; import net.sf.acegisecurity.providers.dao.UsernameNotFoundException; import org.alfresco.error.AlfrescoRuntimeException; diff --git a/source/java/org/alfresco/repo/security/authentication/UpgradePasswordHashWorker.java b/source/java/org/alfresco/repo/security/authentication/UpgradePasswordHashWorker.java index 57afb5c52f..0a38e7aaa7 100644 --- a/source/java/org/alfresco/repo/security/authentication/UpgradePasswordHashWorker.java +++ b/source/java/org/alfresco/repo/security/authentication/UpgradePasswordHashWorker.java @@ -41,10 +41,7 @@ import org.alfresco.repo.lock.JobLockService; import org.alfresco.repo.lock.JobLockService.JobLockRefreshCallback; import org.alfresco.repo.lock.LockAcquisitionException; import org.alfresco.repo.policy.BehaviourFilter; -import org.alfresco.repo.site.SiteModel; import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; @@ -270,6 +267,49 @@ public class UpgradePasswordHashWorker implements ApplicationContextAware, Initi return progress; } + /** + * Processes the user properties, re-hashing the password, if required. + * + * @param properties The properties for the user. + * @return true if the password was upgraded, false if no changes were made. + */ + public boolean processPasswordHash(Map properties) + { + // retrieve the password and hash indicator + Pair, String> passwordHash = RepositoryAuthenticationDao.determinePasswordHash(properties); + + // determine if current password hash matches the preferred encoding + if (!passwordEncoder.lastEncodingIsPreferred(passwordHash.getFirst())) + { + // We need to double hash + List nowHashed = new ArrayList(); + nowHashed.addAll(passwordHash.getFirst()); + nowHashed.add(passwordEncoder.getPreferredEncoding()); + Object salt = properties.get(ContentModel.PROP_SALT); + properties.put(ContentModel.PROP_PASSWORD_HASH, passwordEncoder.encodePreferred(new String(passwordHash.getSecond()), salt)); + properties.put(ContentModel.PROP_HASH_INDICATOR, (Serializable)nowHashed); + properties.remove(ContentModel.PROP_PASSWORD); + properties.remove(ContentModel.PROP_PASSWORD_SHA256); + return true; + } + + // ensure password hash is in the correct place + @SuppressWarnings("unchecked") + List hashIndicator = (List) properties.get(ContentModel.PROP_HASH_INDICATOR); + if (hashIndicator == null) + { + // Already the preferred encoding, just set it + properties.put(ContentModel.PROP_HASH_INDICATOR, (Serializable)passwordHash.getFirst()); + properties.put(ContentModel.PROP_PASSWORD_HASH, passwordHash.getSecond()); + properties.remove(ContentModel.PROP_PASSWORD); + properties.remove(ContentModel.PROP_PASSWORD_SHA256); + return true; + } + + // if we get here no changes were made + return false; + } + /** * @param progress the thread-safe progress */ @@ -459,49 +499,6 @@ public class UpgradePasswordHashWorker implements ApplicationContextAware, Initi { AuthenticationUtil.clearCurrentSecurityContext(); } - - /** - * Processes the user properties, re-hashing the password, if required. - * - * @param properties The properties for the user. - * @return true if the password was upgraded, false if no changes were made. - */ - private boolean processPasswordHash(Map properties) - { - // retrieve the password and hash indicator - Pair, String> passwordHash = RepositoryAuthenticationDao.determinePasswordHash(properties); - - // determine if current password hash matches the preferred encoding - if (!passwordEncoder.lastEncodingIsPreferred(passwordHash.getFirst())) - { - // We need to double hash - List nowHashed = new ArrayList(); - nowHashed.addAll(passwordHash.getFirst()); - nowHashed.add(passwordEncoder.getPreferredEncoding()); - Object salt = properties.get(ContentModel.PROP_SALT); - properties.put(ContentModel.PROP_PASSWORD_HASH, passwordEncoder.encodePreferred(new String(passwordHash.getSecond()), salt)); - properties.put(ContentModel.PROP_HASH_INDICATOR, (Serializable)nowHashed); - properties.remove(ContentModel.PROP_PASSWORD); - properties.remove(ContentModel.PROP_PASSWORD_SHA256); - return true; - } - - // ensure password hash is in the correct place - @SuppressWarnings("unchecked") - List hashIndicator = (List) properties.get(ContentModel.PROP_HASH_INDICATOR); - if (hashIndicator == null) - { - // Already the preferred encoding, just set it - properties.put(ContentModel.PROP_HASH_INDICATOR, (Serializable)passwordHash.getFirst()); - properties.put(ContentModel.PROP_PASSWORD_HASH, passwordHash.getSecond()); - properties.remove(ContentModel.PROP_PASSWORD); - properties.remove(ContentModel.PROP_PASSWORD_SHA256); - return true; - } - - // if we get here no changes were made - return false; - } } /** diff --git a/source/test-java/org/alfresco/AllUnitTestsSuite.java b/source/test-java/org/alfresco/AllUnitTestsSuite.java index b3f328da08..a17ff82c0d 100644 --- a/source/test-java/org/alfresco/AllUnitTestsSuite.java +++ b/source/test-java/org/alfresco/AllUnitTestsSuite.java @@ -102,7 +102,7 @@ public class AllUnitTestsSuite extends TestSuite suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.search.impl.solr.SpellCheckDecisionManagerTest.class)); suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.search.impl.solr.SolrStoreMappingWrapperTest.class)); suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.security.authentication.CompositePasswordEncoderTest.class)); - suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.security.authentication.RepositoryAuthenticationDaoHashingTest.class)); + suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.security.authentication.PasswordHashingTest.class)); suite.addTest(org.alfresco.traitextender.TraitExtenderUnitTestSuite.suite()); suite.addTest(org.alfresco.repo.virtual.VirtualizationUnitTestSuite.suite()); } diff --git a/source/test-java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDaoHashingTest.java b/source/test-java/org/alfresco/repo/security/authentication/PasswordHashingTest.java similarity index 77% rename from source/test-java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDaoHashingTest.java rename to source/test-java/org/alfresco/repo/security/authentication/PasswordHashingTest.java index efde2bde2f..a9be9b0104 100644 --- a/source/test-java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDaoHashingTest.java +++ b/source/test-java/org/alfresco/repo/security/authentication/PasswordHashingTest.java @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ package org.alfresco.repo.security.authentication; import static org.junit.Assert.*; @@ -20,18 +38,18 @@ import java.util.Map; /** * Created by gethin on 01/10/15. */ -public class RepositoryAuthenticationDaoHashingTest +public class PasswordHashingTest { - RepositoryAuthenticationDao authenticationDao; + UpgradePasswordHashWorker passwordHashWorker; CompositePasswordEncoder cpe; @Before public void setUp() throws Exception { - authenticationDao = new RepositoryAuthenticationDao(); cpe = new CompositePasswordEncoder(); cpe.setEncoders(CompositePasswordEncoderTest.encodersConfig); - authenticationDao.setCompositePasswordEncoder(cpe); + passwordHashWorker = new UpgradePasswordHashWorker(); + passwordHashWorker.setCompositePasswordEncoder(cpe); } @Test @@ -48,7 +66,7 @@ public class RepositoryAuthenticationDaoHashingTest properties.put(ContentModel.PROP_PASSWORD, "nonsense"); assertFalse("Should be empty", properties.containsKey(ContentModel.PROP_PASSWORD_HASH)); //No hashing to do but we need to update the Indicator - assertTrue(authenticationDao.rehashedPassword(properties)); + assertTrue(passwordHashWorker.processPasswordHash(properties)); assertEquals(CompositePasswordEncoder.MD4, properties.get(ContentModel.PROP_HASH_INDICATOR)); assertTrue("Should now contain the password", properties.containsKey(ContentModel.PROP_PASSWORD_HASH)); assertFalse("Should remove the property", properties.containsKey(ContentModel.PROP_PASSWORD)); @@ -59,7 +77,7 @@ public class RepositoryAuthenticationDaoHashingTest properties.clear(); properties.put(ContentModel.PROP_PASSWORD, "PLAIN TEXT PASSWORD"); //We don't support plain text. - assertTrue(authenticationDao.rehashedPassword(properties)); + assertTrue(passwordHashWorker.processPasswordHash(properties)); assertEquals(CompositePasswordEncoder.MD4, properties.get(ContentModel.PROP_HASH_INDICATOR)); assertTrue("Should now contain the password", properties.containsKey(ContentModel.PROP_PASSWORD_HASH)); assertFalse("Should remove the property", properties.containsKey(ContentModel.PROP_PASSWORD)); @@ -76,7 +94,7 @@ public class RepositoryAuthenticationDaoHashingTest assertTrue("We have the property", properties.containsKey(ContentModel.PROP_PASSWORD)); assertFalse("Should be empty", properties.containsKey(ContentModel.PROP_PASSWORD_HASH)); //We rehashed this password by taking the md4 and hashing it by bcrypt - assertTrue(authenticationDao.rehashedPassword(properties)); + assertTrue(passwordHashWorker.processPasswordHash(properties)); assertEquals(Arrays.asList("md4","bcrypt10"), properties.get(ContentModel.PROP_HASH_INDICATOR)); assertTrue("Should now contain the password", properties.containsKey(ContentModel.PROP_PASSWORD_HASH)); assertTrue(matches("HASHED_MY_PASSWORD", properties, cpe)); @@ -92,7 +110,7 @@ public class RepositoryAuthenticationDaoHashingTest assertTrue("We have the property", properties.containsKey(ContentModel.PROP_PASSWORD_SHA256)); assertFalse("Should be empty", properties.containsKey(ContentModel.PROP_PASSWORD_HASH)); //We rehashed this password by taking the sha256 and hashing it by bcrypt - assertTrue(authenticationDao.rehashedPassword(properties)); + assertTrue(passwordHashWorker.processPasswordHash(properties)); assertEquals(Arrays.asList("sha256","bcrypt10"), properties.get(ContentModel.PROP_HASH_INDICATOR)); assertTrue("Should now contain the password", properties.containsKey(ContentModel.PROP_PASSWORD_HASH)); assertTrue(matches("HASHED_MY_PASSWORD", properties, cpe)); @@ -107,16 +125,16 @@ public class RepositoryAuthenticationDaoHashingTest properties.put(ContentModel.PROP_HASH_INDICATOR, (Serializable) Arrays.asList("bcrypt10")); properties.put(ContentModel.PROP_PASSWORD_HASH, "long hash"); //Nothing to do. - assertFalse(authenticationDao.rehashedPassword(properties)); + assertFalse(passwordHashWorker.processPasswordHash(properties)); cpe.setPreferredEncoding("bcrypt11"); - assertTrue(authenticationDao.rehashedPassword(properties)); - assertEquals(Arrays.asList("bcrypt10","bcrypt11"),authenticationDao.determinePasswordHash(properties).getFirst()); + assertTrue(passwordHashWorker.processPasswordHash(properties)); + assertEquals(Arrays.asList("bcrypt10","bcrypt11"),RepositoryAuthenticationDao.determinePasswordHash(properties).getFirst()); cpe.setPreferredEncoding("bcrypt12"); - assertTrue(authenticationDao.rehashedPassword(properties)); + assertTrue(passwordHashWorker.processPasswordHash(properties)); //Triple hashing! - assertEquals(Arrays.asList("bcrypt10","bcrypt11","bcrypt12"),authenticationDao.determinePasswordHash(properties).getFirst()); + assertEquals(Arrays.asList("bcrypt10","bcrypt11","bcrypt12"),RepositoryAuthenticationDao.determinePasswordHash(properties).getFirst()); } @Test @@ -128,47 +146,48 @@ public class RepositoryAuthenticationDaoHashingTest try { - authenticationDao.determinePasswordHash(properties); + RepositoryAuthenticationDao.determinePasswordHash(properties); fail("Should throw exception"); } catch (AlfrescoRuntimeException are) { - assertTrue(are.getMessage().contains("Unable to find a user password")); + assertTrue(are.getMessage().contains("Unable to find a password for user")); } //if the PROP_PASSWORD field is the only one availble then we are using MD4 properties.put(ContentModel.PROP_PASSWORD, "mypassword"); - Pair, String> passwordHashed = authenticationDao.determinePasswordHash(properties); + Pair, String> passwordHashed = RepositoryAuthenticationDao.determinePasswordHash(properties); assertEquals(CompositePasswordEncoder.MD4, passwordHashed.getFirst()); assertEquals("mypassword", passwordHashed.getSecond()); //if the PROP_PASSWORD_SHA256 field is used then we are using SHA256 properties.put(ContentModel.PROP_PASSWORD_SHA256, "sha_password"); - passwordHashed = authenticationDao.determinePasswordHash(properties); + passwordHashed = RepositoryAuthenticationDao.determinePasswordHash(properties); assertEquals(CompositePasswordEncoder.SHA256, passwordHashed.getFirst()); assertEquals("sha_password", passwordHashed.getSecond()); properties.put(ContentModel.PROP_HASH_INDICATOR, null); //If the indicator is NULL then it still uses the old password field - passwordHashed = authenticationDao.determinePasswordHash(properties); + passwordHashed = RepositoryAuthenticationDao.determinePasswordHash(properties); assertEquals(CompositePasswordEncoder.SHA256, passwordHashed.getFirst()); assertEquals("sha_password", passwordHashed.getSecond()); properties.put(ContentModel.PROP_HASH_INDICATOR, new ArrayList(0)); //If the indicator doesn't have a value - passwordHashed = authenticationDao.determinePasswordHash(properties); + passwordHashed = RepositoryAuthenticationDao.determinePasswordHash(properties); assertEquals(CompositePasswordEncoder.SHA256, passwordHashed.getFirst()); assertEquals("sha_password", passwordHashed.getSecond()); //Now it uses the correct property properties.put(ContentModel.PROP_HASH_INDICATOR, (Serializable) Arrays.asList("myencoding")); properties.put(ContentModel.PROP_PASSWORD_HASH, "hashed this time"); - passwordHashed = authenticationDao.determinePasswordHash(properties); + passwordHashed = RepositoryAuthenticationDao.determinePasswordHash(properties); assertEquals(Arrays.asList("myencoding"), passwordHashed.getFirst()); assertEquals("hashed this time",passwordHashed.getSecond()); } + @SuppressWarnings("unchecked") private static boolean matches(String password, Map properties, CompositePasswordEncoder cpe) { return cpe.matchesPassword(password, (String) properties.get(ContentModel.PROP_PASSWORD_HASH), (String) properties.get(ContentModel.PROP_SALT), (List) properties.get(ContentModel.PROP_HASH_INDICATOR) ); diff --git a/source/test-java/org/alfresco/repo/security/authentication/UpgradePasswordHashTest.java b/source/test-java/org/alfresco/repo/security/authentication/UpgradePasswordHashTest.java index 696c5117ea..e5c6d791ea 100644 --- a/source/test-java/org/alfresco/repo/security/authentication/UpgradePasswordHashTest.java +++ b/source/test-java/org/alfresco/repo/security/authentication/UpgradePasswordHashTest.java @@ -19,7 +19,6 @@ package org.alfresco.repo.security.authentication; import java.io.Serializable; -import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -44,7 +43,6 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.QName; import org.alfresco.test_category.OwnJVMTestsCategory; import org.alfresco.util.ApplicationContextHelper; -import org.alfresco.util.GUID; import org.junit.experimental.categories.Category; import org.springframework.context.ApplicationContext;