diff --git a/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java index 3875d8f4a7..29b9f24814 100644 --- a/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java @@ -41,7 +41,6 @@ import org.springframework.dao.DataAccessException; * getMD4HashedPassword(String userName) * loadUserByUsername(String arg0) * getSalt(UserDetails user) - * hashUserPassword(String userName) * * @author Andy Hind */ @@ -386,15 +385,6 @@ public class DefaultMutableAuthenticationDao implements MutableAuthenticationDao throw new AlfrescoRuntimeException("Not implemented"); } - /** - * @throws AlfrescoRuntimeException always - */ - @Override - public void hashUserPassword(String userName) throws AuthenticationException - { - throw new AlfrescoRuntimeException("Not implemented"); - } - // -------- // // Bean IOC // // -------- // diff --git a/source/java/org/alfresco/repo/security/authentication/MutableAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/MutableAuthenticationDao.java index f8476938d8..a368668850 100644 --- a/source/java/org/alfresco/repo/security/authentication/MutableAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/MutableAuthenticationDao.java @@ -50,11 +50,6 @@ public interface MutableAuthenticationDao extends AuthenticationDao, SaltSource * Check is a user exists. */ public boolean userExists(String userName); - - /** - * Hashes the user password to the preferred encoding. - */ - public void hashUserPassword(String userName) throws AuthenticationException; /** * Enable/disable a user. diff --git a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java index 8c4b37debc..9855595cb5 100644 --- a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java @@ -285,51 +285,16 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In // Always use a transaction return transactionService.getRetryingTransactionHelper().doInTransaction(new SearchUserNameCallback(), true); } - + /** - * Should we rehash the password by updating the properties - * @param properties - * @return + * Retrieves the password hash for the given user properties. + * + * @param properties The properties of the user. + * @return A Pair object containing the hash indicator and the hashed password. */ - public boolean rehashedPassword(Map properties) - { - List hashIndicator = (List) properties.get(ContentModel.PROP_HASH_INDICATOR); - Pair, String> passwordHash = determinePasswordHash(properties); - - if (!compositePasswordEncoder.lastEncodingIsPreferred(passwordHash.getFirst())) - { - //We need to double hash - List nowHashed = new ArrayList(); - nowHashed.addAll(passwordHash.getFirst()); - nowHashed.add(compositePasswordEncoder.getPreferredEncoding()); - Object salt = properties.get(ContentModel.PROP_SALT); - properties.put(ContentModel.PROP_PASSWORD_HASH, compositePasswordEncoder.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; - } - - 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; - } - - return false; - } - - /** - * Where is the password and how is it encoded? - * @param properties - * @return - */ - protected Pair, String> determinePasswordHash(Map properties) + public static Pair, String> determinePasswordHash(Map properties) { + @SuppressWarnings("unchecked") List hashIndicator = (List) properties.get(ContentModel.PROP_HASH_INDICATOR); if (hashIndicator != null && hashIndicator.size()>0) { @@ -354,8 +319,11 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In } } } - throw new AlfrescoRuntimeException("Unable to find a user password, please check your repository authentication settings." - + "(PreferredEncoding="+compositePasswordEncoder.getPreferredEncoding()+")"); + + // throw execption if we failed to find a password for the user + throw new AlfrescoRuntimeException("Unable to find a password for user '" + + properties.get(ContentModel.PROP_USER_USERNAME) + + "', please check your repository authentication settings."); } @Override @@ -469,22 +437,6 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In return (getUserOrNull(userName) != null); } - @Override - public void hashUserPassword(String userName) throws AuthenticationException - { - NodeRef userRef = getUserOrNull(userName); - if (userRef == null) - { - throw new AuthenticationException("User name does not exist: " + userName); - } - Map properties = nodeService.getProperties(userRef); - - if (rehashedPassword(properties)) - { - nodeService.setProperties(userRef, properties); - } - } - /** * @return Returns the user properties or null if there are none */ diff --git a/source/java/org/alfresco/repo/security/authentication/UpgradePasswordHashWorker.java b/source/java/org/alfresco/repo/security/authentication/UpgradePasswordHashWorker.java index 64e4259441..57afb5c52f 100644 --- a/source/java/org/alfresco/repo/security/authentication/UpgradePasswordHashWorker.java +++ b/source/java/org/alfresco/repo/security/authentication/UpgradePasswordHashWorker.java @@ -19,6 +19,7 @@ package org.alfresco.repo.security.authentication; import java.io.Serializable; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -43,6 +44,7 @@ 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; @@ -387,7 +389,13 @@ public class UpgradePasswordHashWorker implements ApplicationContextAware, Initi this.progress = progress; } - @SuppressWarnings("unchecked") + @Override + public void beforeProcess() throws Throwable + { + // Run as the systemuser + AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName()); + } + @Override public void process(Long nodeId) throws Throwable { @@ -398,26 +406,32 @@ public class UpgradePasswordHashWorker implements ApplicationContextAware, Initi // get properties for the user Map userProps = nodeDAO.getNodeProperties(nodeId); - // get the hash indicator property - List hashIndicator = (List)userProps.get(ContentModel.PROP_HASH_INDICATOR); - // get the username String username = (String)userProps.get(ContentModel.PROP_USER_USERNAME); - // determine whether we need to upgrade the password hash for the user - if (hashIndicator == null || !passwordEncoder.lastEncodingIsPreferred(hashIndicator)) + // determine whether the password requires re-hashing + if (processPasswordHash(userProps)) { progress.usersChanged.incrementAndGet(); - // We do not want any behaviours associated with our transactions - behaviourFilter.disableBehaviour(); - - if (logger.isDebugEnabled()) + try { - logger.debug("Upgrading password hash for user: " + username); - } - authenticationDao.hashUserPassword(username); + // disable auditing + behaviourFilter.disableBehaviour(); + if (logger.isDebugEnabled()) + { + logger.debug("Upgrading password hash for user: " + username); + } + + // persist the changes + nodeDAO.setNodeProperties(nodeId, userProps); + } + finally + { + // enable auditing + behaviourFilter.enableBehaviour(); + } } else if (logger.isTraceEnabled()) { @@ -439,6 +453,55 @@ public class UpgradePasswordHashWorker implements ApplicationContextAware, Initi { return (String)nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_USER_USERNAME); } + + @Override + public void afterProcess() throws Throwable + { + 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/java/org/alfresco/repo/security/authentication/ntlm/NullMutableAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/ntlm/NullMutableAuthenticationDao.java index 3a230da54a..02353c29a2 100644 --- a/source/java/org/alfresco/repo/security/authentication/ntlm/NullMutableAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/ntlm/NullMutableAuthenticationDao.java @@ -237,15 +237,6 @@ public class NullMutableAuthenticationDao implements MutableAuthenticationDao throw new AlfrescoRuntimeException("Not implemented"); } - /** - * @throws AlfrescoRuntimeException always - */ - @Override - public void hashUserPassword(String userName) throws AuthenticationException - { - throw new AlfrescoRuntimeException("Not implemented"); - } - /** * @throws AlfrescoRuntimeException Not implemented */ 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 5f371d2bac..696c5117ea 100644 --- a/source/test-java/org/alfresco/repo/security/authentication/UpgradePasswordHashTest.java +++ b/source/test-java/org/alfresco/repo/security/authentication/UpgradePasswordHashTest.java @@ -108,6 +108,8 @@ public class UpgradePasswordHashTest extends TestCase AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); createTestUsers("md4"); + + userTransaction.commit(); } protected void createTestUsers(String encoding) throws Exception @@ -183,6 +185,9 @@ public class UpgradePasswordHashTest extends TestCase public void testWorkerWithDefaultConfiguration() throws Exception { + userTransaction = serviceRegistry.getTransactionService().getUserTransaction(); + userTransaction.begin(); + for (NodeRef testUser : testUsers) { assertNull("The hash indicator should not be set",nodeService.getProperty(testUser, ContentModel.PROP_HASH_INDICATOR)); @@ -191,6 +196,10 @@ public class UpgradePasswordHashTest extends TestCase // execute the worker to upgrade all users this.upgradePasswordHashWorker.execute(); + userTransaction.commit(); + userTransaction = serviceRegistry.getTransactionService().getUserTransaction(); + userTransaction.begin(); + // ensure all the test users have been upgraded to use the preferred encoding List doubleHashed = Arrays.asList("md4", "bcrypt10"); for (NodeRef testUser : testUsers) @@ -200,7 +209,6 @@ public class UpgradePasswordHashTest extends TestCase assertNull("The md4 password should not be set", nodeService.getProperty(testUser, ContentModel.PROP_PASSWORD)); assertNull("The sh256 password should not be set",nodeService.getProperty(testUser, ContentModel.PROP_PASSWORD_SHA256)); } - } public void xxxtestWorkerWithLegacyConfiguration() throws Exception