mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-08 14:51:49 +00:00
Merged 5.0.2-CLOUD42 (Cloud ) to 5.1.N (5.1.1)
117248 adavis: Merged 5.0.2-CLOUD (Cloud ) to 5.0.2-CLOUD42 (Cloud ) 114517 adavis: Merged BCRYPT to 5.0.2-CLOUD 114012 gcornwell: MNT-14892: Updates to upgrade password hash job and test. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.1.N/root@117340 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -41,7 +41,6 @@ import org.springframework.dao.DataAccessException;
|
|||||||
* getMD4HashedPassword(String userName)
|
* getMD4HashedPassword(String userName)
|
||||||
* loadUserByUsername(String arg0)
|
* loadUserByUsername(String arg0)
|
||||||
* getSalt(UserDetails user)
|
* getSalt(UserDetails user)
|
||||||
* hashUserPassword(String userName)
|
|
||||||
*
|
*
|
||||||
* @author Andy Hind
|
* @author Andy Hind
|
||||||
*/
|
*/
|
||||||
@@ -386,15 +385,6 @@ public class DefaultMutableAuthenticationDao implements MutableAuthenticationDao
|
|||||||
throw new AlfrescoRuntimeException("Not implemented");
|
throw new AlfrescoRuntimeException("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws AlfrescoRuntimeException always
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void hashUserPassword(String userName) throws AuthenticationException
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------- //
|
// -------- //
|
||||||
// Bean IOC //
|
// Bean IOC //
|
||||||
// -------- //
|
// -------- //
|
||||||
|
@@ -51,11 +51,6 @@ public interface MutableAuthenticationDao extends AuthenticationDao, SaltSource
|
|||||||
*/
|
*/
|
||||||
public boolean userExists(String userName);
|
public boolean userExists(String userName);
|
||||||
|
|
||||||
/**
|
|
||||||
* Hashes the user password to the preferred encoding.
|
|
||||||
*/
|
|
||||||
public void hashUserPassword(String userName) throws AuthenticationException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable/disable a user.
|
* Enable/disable a user.
|
||||||
*/
|
*/
|
||||||
|
@@ -287,49 +287,14 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should we rehash the password by updating the properties
|
* Retrieves the password hash for the given user properties.
|
||||||
* @param properties
|
*
|
||||||
* @return
|
* @param properties The properties of the user.
|
||||||
|
* @return A Pair object containing the hash indicator and the hashed password.
|
||||||
*/
|
*/
|
||||||
public boolean rehashedPassword(Map<QName, Serializable> properties)
|
public static Pair<List<String>, String> determinePasswordHash(Map<QName, Serializable> properties)
|
||||||
{
|
|
||||||
List<String> hashIndicator = (List<String>) properties.get(ContentModel.PROP_HASH_INDICATOR);
|
|
||||||
Pair<List<String>, String> passwordHash = determinePasswordHash(properties);
|
|
||||||
|
|
||||||
if (!compositePasswordEncoder.lastEncodingIsPreferred(passwordHash.getFirst()))
|
|
||||||
{
|
|
||||||
//We need to double hash
|
|
||||||
List<String> nowHashed = new ArrayList<String>();
|
|
||||||
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<List<String>, String> determinePasswordHash(Map<QName, Serializable> properties)
|
|
||||||
{
|
{
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
List<String> hashIndicator = (List<String>) properties.get(ContentModel.PROP_HASH_INDICATOR);
|
List<String> hashIndicator = (List<String>) properties.get(ContentModel.PROP_HASH_INDICATOR);
|
||||||
if (hashIndicator != null && hashIndicator.size()>0)
|
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
|
@Override
|
||||||
@@ -469,22 +437,6 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In
|
|||||||
return (getUserOrNull(userName) != null);
|
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<QName, Serializable> properties = nodeService.getProperties(userRef);
|
|
||||||
|
|
||||||
if (rehashedPassword(properties))
|
|
||||||
{
|
|
||||||
nodeService.setProperties(userRef, properties);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns the user properties or <tt>null</tt> if there are none
|
* @return Returns the user properties or <tt>null</tt> if there are none
|
||||||
*/
|
*/
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
package org.alfresco.repo.security.authentication;
|
package org.alfresco.repo.security.authentication;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -43,6 +44,7 @@ import org.alfresco.repo.policy.BehaviourFilter;
|
|||||||
import org.alfresco.repo.site.SiteModel;
|
import org.alfresco.repo.site.SiteModel;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
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.NamespaceService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
@@ -387,7 +389,13 @@ public class UpgradePasswordHashWorker implements ApplicationContextAware, Initi
|
|||||||
this.progress = progress;
|
this.progress = progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@Override
|
||||||
|
public void beforeProcess() throws Throwable
|
||||||
|
{
|
||||||
|
// Run as the systemuser
|
||||||
|
AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(Long nodeId) throws Throwable
|
public void process(Long nodeId) throws Throwable
|
||||||
{
|
{
|
||||||
@@ -398,26 +406,32 @@ public class UpgradePasswordHashWorker implements ApplicationContextAware, Initi
|
|||||||
// get properties for the user
|
// get properties for the user
|
||||||
Map<QName, Serializable> userProps = nodeDAO.getNodeProperties(nodeId);
|
Map<QName, Serializable> userProps = nodeDAO.getNodeProperties(nodeId);
|
||||||
|
|
||||||
// get the hash indicator property
|
|
||||||
List<String> hashIndicator = (List<String>)userProps.get(ContentModel.PROP_HASH_INDICATOR);
|
|
||||||
|
|
||||||
// get the username
|
// get the username
|
||||||
String username = (String)userProps.get(ContentModel.PROP_USER_USERNAME);
|
String username = (String)userProps.get(ContentModel.PROP_USER_USERNAME);
|
||||||
|
|
||||||
// determine whether we need to upgrade the password hash for the user
|
// determine whether the password requires re-hashing
|
||||||
if (hashIndicator == null || !passwordEncoder.lastEncodingIsPreferred(hashIndicator))
|
if (processPasswordHash(userProps))
|
||||||
{
|
{
|
||||||
progress.usersChanged.incrementAndGet();
|
progress.usersChanged.incrementAndGet();
|
||||||
|
|
||||||
// We do not want any behaviours associated with our transactions
|
try
|
||||||
behaviourFilter.disableBehaviour();
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled())
|
|
||||||
{
|
{
|
||||||
logger.debug("Upgrading password hash for user: " + username);
|
// disable auditing
|
||||||
}
|
behaviourFilter.disableBehaviour();
|
||||||
authenticationDao.hashUserPassword(username);
|
|
||||||
|
|
||||||
|
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())
|
else if (logger.isTraceEnabled())
|
||||||
{
|
{
|
||||||
@@ -439,6 +453,55 @@ public class UpgradePasswordHashWorker implements ApplicationContextAware, Initi
|
|||||||
{
|
{
|
||||||
return (String)nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_USER_USERNAME);
|
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<QName, Serializable> properties)
|
||||||
|
{
|
||||||
|
// retrieve the password and hash indicator
|
||||||
|
Pair<List<String>, 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<String> nowHashed = new ArrayList<String>();
|
||||||
|
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<String> hashIndicator = (List<String>) 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -237,15 +237,6 @@ public class NullMutableAuthenticationDao implements MutableAuthenticationDao
|
|||||||
throw new AlfrescoRuntimeException("Not implemented");
|
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
|
* @throws AlfrescoRuntimeException Not implemented
|
||||||
*/
|
*/
|
||||||
|
@@ -108,6 +108,8 @@ public class UpgradePasswordHashTest extends TestCase
|
|||||||
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName());
|
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName());
|
||||||
|
|
||||||
createTestUsers("md4");
|
createTestUsers("md4");
|
||||||
|
|
||||||
|
userTransaction.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void createTestUsers(String encoding) throws Exception
|
protected void createTestUsers(String encoding) throws Exception
|
||||||
@@ -183,6 +185,9 @@ public class UpgradePasswordHashTest extends TestCase
|
|||||||
|
|
||||||
public void testWorkerWithDefaultConfiguration() throws Exception
|
public void testWorkerWithDefaultConfiguration() throws Exception
|
||||||
{
|
{
|
||||||
|
userTransaction = serviceRegistry.getTransactionService().getUserTransaction();
|
||||||
|
userTransaction.begin();
|
||||||
|
|
||||||
for (NodeRef testUser : testUsers)
|
for (NodeRef testUser : testUsers)
|
||||||
{
|
{
|
||||||
assertNull("The hash indicator should not be set",nodeService.getProperty(testUser, ContentModel.PROP_HASH_INDICATOR));
|
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
|
// execute the worker to upgrade all users
|
||||||
this.upgradePasswordHashWorker.execute();
|
this.upgradePasswordHashWorker.execute();
|
||||||
|
|
||||||
|
userTransaction.commit();
|
||||||
|
userTransaction = serviceRegistry.getTransactionService().getUserTransaction();
|
||||||
|
userTransaction.begin();
|
||||||
|
|
||||||
// ensure all the test users have been upgraded to use the preferred encoding
|
// ensure all the test users have been upgraded to use the preferred encoding
|
||||||
List<String> doubleHashed = Arrays.asList("md4", "bcrypt10");
|
List<String> doubleHashed = Arrays.asList("md4", "bcrypt10");
|
||||||
for (NodeRef testUser : testUsers)
|
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 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));
|
assertNull("The sh256 password should not be set",nodeService.getProperty(testUser, ContentModel.PROP_PASSWORD_SHA256));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void xxxtestWorkerWithLegacyConfiguration() throws Exception
|
public void xxxtestWorkerWithLegacyConfiguration() throws Exception
|
||||||
|
Reference in New Issue
Block a user