mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-08 14:51:49 +00:00
Merged 5.1-MC1 (5.1.0) to HEAD (5.1)
119056 adavis: Merged 5.1.N (5.1.1) to 5.1-MC1 (5.1.0) 117338 adavis: Merged 5.0.2-CLOUD42 (Cloud ) to 5.1.N (5.1.1) 117246 adavis: Merged 5.0.2-CLOUD (Cloud ) to 5.0.2-CLOUD42 (Cloud ) 114515 adavis: Merged BCRYPT to 5.0.2-CLOUD 113961 gcornwell: MNT-14892: Added UpgradePasswordHashWorker job and skeleton test class git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@119895 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -695,4 +695,41 @@
|
|||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<!-- UpgradePasswordHashWorker -->
|
||||||
|
<bean id="upgradePasswordHashWorker" class="org.alfresco.repo.security.authentication.UpgradePasswordHashWorker">
|
||||||
|
<property name="jobLockService">
|
||||||
|
<ref bean="jobLockService" />
|
||||||
|
</property>
|
||||||
|
<property name="transactionService">
|
||||||
|
<ref bean="transactionService" />
|
||||||
|
</property>
|
||||||
|
<property name="authenticationDao">
|
||||||
|
<ref bean="authenticationDao" />
|
||||||
|
</property>
|
||||||
|
<property name="compositePasswordEncoder">
|
||||||
|
<ref bean="compositePasswordEncoder" />
|
||||||
|
</property>
|
||||||
|
<property name="behaviourFilter">
|
||||||
|
<ref bean="policyBehaviourFilter" />
|
||||||
|
</property>
|
||||||
|
<property name="patchDAO">
|
||||||
|
<ref bean="patchDAO"/>
|
||||||
|
</property>
|
||||||
|
<property name="nodeDAO">
|
||||||
|
<ref bean="nodeDAO"/>
|
||||||
|
</property>
|
||||||
|
<property name="qnameDAO">
|
||||||
|
<ref bean="qnameDAO"/>
|
||||||
|
</property>
|
||||||
|
<property name="queryRange">
|
||||||
|
<value>${system.upgradePasswordHash.jobQueryRange}</value>
|
||||||
|
</property>
|
||||||
|
<property name="threadCount">
|
||||||
|
<value>${system.upgradePasswordHash.jobThreadCount}</value>
|
||||||
|
</property>
|
||||||
|
<property name="batchSize">
|
||||||
|
<value>${system.upgradePasswordHash.jobBatchSize}</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
@@ -1107,6 +1107,12 @@ links.protocosl.white.list=http,https,ftp,mailto
|
|||||||
|
|
||||||
cmis.disable.hidden.leading.period.files=false
|
cmis.disable.hidden.leading.period.files=false
|
||||||
|
|
||||||
|
# Upgrade Password Hash Job
|
||||||
|
system.upgradePasswordHash.jobBatchSize=100
|
||||||
|
system.upgradePasswordHash.jobQueryRange=10000
|
||||||
|
system.upgradePasswordHash.jobThreadCount=4
|
||||||
|
system.upgradePasswordHash.jobCronExpression=* * * * * ? 2099
|
||||||
|
|
||||||
#Virtual Folders Config Properties
|
#Virtual Folders Config Properties
|
||||||
|
|
||||||
virtual.folders.enabled=false
|
virtual.folders.enabled=false
|
||||||
|
@@ -281,4 +281,24 @@
|
|||||||
<value>${system.cronJob.startDelayMinutes}</value>
|
<value>${system.cronJob.startDelayMinutes}</value>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<!-- Definition for the upgrade password hash job -->
|
||||||
|
<!--
|
||||||
|
<bean id="upgradePasswordHashJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
|
||||||
|
<property name="jobClass"
|
||||||
|
value="org.alfresco.repo.security.authenticationUpgradePasswordHashWorker$UpgradePasswordHashJob" />
|
||||||
|
<property name="jobDataAsMap">
|
||||||
|
<map>
|
||||||
|
<entry key="upgradePasswordHashWorker" value-ref="upgradePasswordHashWorker" />
|
||||||
|
</map>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
<bean id="upgradePasswordHashJobTrigger" class="org.alfresco.util.CronTriggerBean">
|
||||||
|
<property name="jobDetail" ref="upgradePasswordHashJobDetail" />
|
||||||
|
<property name="scheduler" ref="schedulerFactory" />
|
||||||
|
<property name="cronExpression" value="${system.upgradePasswordHash.jobCronExpression}" />
|
||||||
|
<property name="startDelayMinutes" value="${system.cronJob.startDelayMinutes}" />
|
||||||
|
</bean>
|
||||||
|
-->
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
@@ -0,0 +1,500 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.security.authentication;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.repo.batch.BatchProcessWorkProvider;
|
||||||
|
import org.alfresco.repo.batch.BatchProcessor;
|
||||||
|
import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorker;
|
||||||
|
import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorkerAdaptor;
|
||||||
|
import org.alfresco.repo.domain.node.NodeDAO;
|
||||||
|
import org.alfresco.repo.domain.patch.PatchDAO;
|
||||||
|
import org.alfresco.repo.domain.qname.QNameDAO;
|
||||||
|
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.namespace.NamespaceService;
|
||||||
|
import org.alfresco.service.namespace.QName;
|
||||||
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
|
import org.alfresco.util.Pair;
|
||||||
|
import org.alfresco.util.ParameterCheck;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.quartz.Job;
|
||||||
|
import org.quartz.JobDataMap;
|
||||||
|
import org.quartz.JobExecutionContext;
|
||||||
|
import org.quartz.JobExecutionException;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h1>Upgrade Password Hash Worker</h1>
|
||||||
|
*
|
||||||
|
* <h2>What it is</h2>
|
||||||
|
* A worker for a scheduled job that checks and upgrades users passwords to the system's preferred encoding.
|
||||||
|
*
|
||||||
|
* <h2>Settings that control the behaviour</h2>
|
||||||
|
* <ul>
|
||||||
|
* <li><b>${system.upgradePasswordHash.jobBatchSize}</b> - the number of users to process at one time.</li>
|
||||||
|
* <li><b>${system.upgradePasswordHash.jobQueryRange}</b> - the node ID range to query for.
|
||||||
|
* The process will repeat from the first to the last node, querying for up to this many nodes.
|
||||||
|
* Only reduce the value if the NodeDAO query takes a long time.</li>
|
||||||
|
* <li><b>${system.upgradePasswordHash.jobThreadCount}</b> - the number of threads that will handle user checks and changes.
|
||||||
|
* Increase or decrease this to allow for free CPU capacity on the machine executing the job.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Gavin Cornwell
|
||||||
|
*/
|
||||||
|
public class UpgradePasswordHashWorker implements ApplicationContextAware, InitializingBean
|
||||||
|
{
|
||||||
|
private static final QName LOCK = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "UpgradePasswordHashWorker");
|
||||||
|
private static final long LOCK_TTL = 60000L;
|
||||||
|
|
||||||
|
private static Log logger = LogFactory.getLog(UpgradePasswordHashWorker.class);
|
||||||
|
|
||||||
|
private JobLockService jobLockService;
|
||||||
|
private TransactionService transactionService;
|
||||||
|
|
||||||
|
private MutableAuthenticationDao authenticationDao;
|
||||||
|
private CompositePasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
private NodeDAO nodeDAO;
|
||||||
|
private PatchDAO patchDAO;
|
||||||
|
private QNameDAO qnameDAO;
|
||||||
|
|
||||||
|
private BehaviourFilter behaviourFilter;
|
||||||
|
private ApplicationContext ctx;
|
||||||
|
|
||||||
|
private int queryRange = 10000;
|
||||||
|
private int threadCount = 2;
|
||||||
|
private int batchSize = 100;
|
||||||
|
|
||||||
|
public void setJobLockService(JobLockService jobLockService)
|
||||||
|
{
|
||||||
|
this.jobLockService = jobLockService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTransactionService(TransactionService transactionService)
|
||||||
|
{
|
||||||
|
this.transactionService = transactionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthenticationDao(MutableAuthenticationDao authenticationDao)
|
||||||
|
{
|
||||||
|
this.authenticationDao = authenticationDao;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCompositePasswordEncoder(CompositePasswordEncoder passwordEncoder)
|
||||||
|
{
|
||||||
|
this.passwordEncoder = passwordEncoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPatchDAO(PatchDAO patchDAO)
|
||||||
|
{
|
||||||
|
this.patchDAO = patchDAO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeDAO(NodeDAO nodeDAO)
|
||||||
|
{
|
||||||
|
this.nodeDAO = nodeDAO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQnameDAO(QNameDAO qnameDAO)
|
||||||
|
{
|
||||||
|
this.qnameDAO = qnameDAO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBehaviourFilter(BehaviourFilter behaviourFilter)
|
||||||
|
{
|
||||||
|
this.behaviourFilter = behaviourFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the number of users to retrieve from the repository in each query.
|
||||||
|
*
|
||||||
|
* @param queryRange The query range
|
||||||
|
*/
|
||||||
|
public void setQueryRange(int queryRange)
|
||||||
|
{
|
||||||
|
this.queryRange = queryRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the number of threads to use to process users.
|
||||||
|
*
|
||||||
|
* @param threadCount Number of threads
|
||||||
|
*/
|
||||||
|
public void setThreadCount(int threadCount)
|
||||||
|
{
|
||||||
|
this.threadCount = threadCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the number of users to process at one time.
|
||||||
|
*
|
||||||
|
* @param batchSize The batch size
|
||||||
|
*/
|
||||||
|
public void setBatchSize(int batchSize)
|
||||||
|
{
|
||||||
|
this.batchSize = batchSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the application context for event publishing during batch processing
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
|
||||||
|
{
|
||||||
|
this.ctx = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception
|
||||||
|
{
|
||||||
|
ParameterCheck.mandatory("jobLockService", jobLockService);
|
||||||
|
ParameterCheck.mandatory("transactionService", transactionService);
|
||||||
|
ParameterCheck.mandatory("authenticationDao", authenticationDao);
|
||||||
|
ParameterCheck.mandatory("compositePasswordEncoder", passwordEncoder);
|
||||||
|
ParameterCheck.mandatory("nodeDAO", nodeDAO);
|
||||||
|
ParameterCheck.mandatory("patchDAO", patchDAO);
|
||||||
|
ParameterCheck.mandatory("qnameDAO", qnameDAO);
|
||||||
|
ParameterCheck.mandatory("behaviourFilter", behaviourFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the work, including logging details of progress.
|
||||||
|
*/
|
||||||
|
public UpgradePasswordHashWorkResult execute()
|
||||||
|
{
|
||||||
|
// Build refresh callback
|
||||||
|
final UpgradePasswordHashWorkResult progress = new UpgradePasswordHashWorkResult();
|
||||||
|
JobLockRefreshCallback lockCallback = new JobLockRefreshCallback()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void lockReleased()
|
||||||
|
{
|
||||||
|
progress.inProgress.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive()
|
||||||
|
{
|
||||||
|
return progress.inProgress.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
String lockToken = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
progress.inProgress.set(true);
|
||||||
|
|
||||||
|
// Get the lock
|
||||||
|
lockToken = jobLockService.getLock(LOCK, LOCK_TTL);
|
||||||
|
|
||||||
|
// Start the refresh timer
|
||||||
|
jobLockService.refreshLock(lockToken, LOCK, LOCK_TTL, lockCallback);
|
||||||
|
|
||||||
|
// Now we know that we'll do something
|
||||||
|
if (logger.isInfoEnabled())
|
||||||
|
{
|
||||||
|
logger.info("Starting upgrade password hash job.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the work
|
||||||
|
doWork(progress);
|
||||||
|
|
||||||
|
// Done
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Upgrade password hash job " + progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (LockAcquisitionException e)
|
||||||
|
{
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Skipping upgrade password hash job: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
progress.inProgress.set(false);
|
||||||
|
logger.error("Upgrade password hash job " + progress);
|
||||||
|
logger.error("Stopping upgrade password hash job with exception.", e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (lockToken != null)
|
||||||
|
{
|
||||||
|
jobLockService.releaseLock(lockToken, LOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
progress.inProgress.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param progress the thread-safe progress
|
||||||
|
*/
|
||||||
|
private synchronized void doWork(UpgradePasswordHashWorkResult progress) throws Exception
|
||||||
|
{
|
||||||
|
// Build batch processor
|
||||||
|
BatchProcessWorkProvider<Long> workProvider = new UpgradePasswordHashWorkProvider(progress);
|
||||||
|
BatchProcessWorker<Long> worker = new UpgradePasswordHashBatch(progress);
|
||||||
|
RetryingTransactionHelper retryingTransactionHelper = transactionService.getRetryingTransactionHelper();
|
||||||
|
retryingTransactionHelper.setForceWritable(true);
|
||||||
|
|
||||||
|
BatchProcessor<Long> batchProcessor = new BatchProcessor<Long>(
|
||||||
|
"UpgradePasswordHashWorker",
|
||||||
|
retryingTransactionHelper,
|
||||||
|
workProvider,
|
||||||
|
threadCount,
|
||||||
|
batchSize,
|
||||||
|
ctx,
|
||||||
|
logger,
|
||||||
|
1000);
|
||||||
|
batchProcessor.process(worker, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Work provider for batch job providing noderefs representing users to process
|
||||||
|
*/
|
||||||
|
private class UpgradePasswordHashWorkProvider implements BatchProcessWorkProvider<Long>
|
||||||
|
{
|
||||||
|
private final long maxNodeId;
|
||||||
|
private final UpgradePasswordHashWorkResult progress;
|
||||||
|
private final Pair<Long, QName> userTypeId;
|
||||||
|
|
||||||
|
private UpgradePasswordHashWorkProvider(UpgradePasswordHashWorkResult progress)
|
||||||
|
{
|
||||||
|
this.progress = progress;
|
||||||
|
this.maxNodeId = patchDAO.getMaxAdmNodeID();
|
||||||
|
this.userTypeId = qnameDAO.getQName(ContentModel.TYPE_USER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTotalEstimatedWorkSize()
|
||||||
|
{
|
||||||
|
// execute a query to get total number of user nodes in the system.
|
||||||
|
long totalUserCount = patchDAO.getCountNodesWithTypId(ContentModel.TYPE_USER);
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Max NodeID: " + this.maxNodeId);
|
||||||
|
logger.debug("Total number of users: " + totalUserCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)totalUserCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Long> getNextWork()
|
||||||
|
{
|
||||||
|
// Check that there are not too many errors
|
||||||
|
if (progress.errors.get() > 1000)
|
||||||
|
{
|
||||||
|
logger.warn("Upgrade password hash work terminating; too many errors.");
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep shifting the query window up until we get results or we hit the original max node ID
|
||||||
|
List<Long> ret = Collections.emptyList();
|
||||||
|
while (ret.isEmpty() && progress.currentMinNodeId.get() < maxNodeId)
|
||||||
|
{
|
||||||
|
// Calculate the node ID range
|
||||||
|
Long minNodeId = null;
|
||||||
|
if (progress.currentMinNodeId.get() == 0L)
|
||||||
|
{
|
||||||
|
minNodeId = 1L;
|
||||||
|
progress.currentMinNodeId.set(minNodeId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
minNodeId = progress.currentMinNodeId.addAndGet(queryRange);
|
||||||
|
}
|
||||||
|
long maxNodeId = minNodeId + queryRange;
|
||||||
|
|
||||||
|
// Query for the next set of users
|
||||||
|
ret = patchDAO.getNodesByTypeQNameId(this.userTypeId.getFirst(), minNodeId, maxNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Upgrade password hash work provider found " + ret.size() + " users.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that does the actual node manipulation to upgrade the password hash.
|
||||||
|
*/
|
||||||
|
private class UpgradePasswordHashBatch extends BatchProcessWorkerAdaptor<Long>
|
||||||
|
{
|
||||||
|
private final UpgradePasswordHashWorkResult progress;
|
||||||
|
|
||||||
|
private UpgradePasswordHashBatch(UpgradePasswordHashWorkResult progress)
|
||||||
|
{
|
||||||
|
this.progress = progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void process(Long nodeId) throws Throwable
|
||||||
|
{
|
||||||
|
progress.usersProcessed.incrementAndGet();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// get properties for the user
|
||||||
|
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
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
progress.usersChanged.incrementAndGet();
|
||||||
|
|
||||||
|
// We do not want any behaviours associated with our transactions
|
||||||
|
behaviourFilter.disableBehaviour();
|
||||||
|
|
||||||
|
// call hashedPassword on the RepositoryAuthenticationDao object
|
||||||
|
|
||||||
|
|
||||||
|
// ((RepositoryAuthenticationDao)authenticationDao).rehashedPassword(userProps);
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Upgrading password hash for user: " + username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (logger.isTraceEnabled())
|
||||||
|
{
|
||||||
|
logger.trace("User '" + username + "' has preferred encoding");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// Record the failure
|
||||||
|
progress.errors.incrementAndGet();
|
||||||
|
|
||||||
|
// Rethrow so that the processing framework can handle things
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getIdentifier(Long nodeId)
|
||||||
|
{
|
||||||
|
return (String)nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_USER_USERNAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread-safe helper class to carry the job progress information.
|
||||||
|
*/
|
||||||
|
public static class UpgradePasswordHashWorkResult
|
||||||
|
{
|
||||||
|
private final AtomicBoolean inProgress = new AtomicBoolean(false);
|
||||||
|
private final AtomicInteger usersProcessed = new AtomicInteger(0);
|
||||||
|
private final AtomicInteger usersChanged = new AtomicInteger(0);
|
||||||
|
private final AtomicInteger errors = new AtomicInteger(0);
|
||||||
|
private final AtomicLong currentMinNodeId = new AtomicLong(0L);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
String part1 = "Changed";
|
||||||
|
String part2 = String.format(" %4d out of a potential %4d users. ", usersChanged.get(), usersProcessed.get());
|
||||||
|
String part3 = String.format("[%2d Errors]", errors.get());
|
||||||
|
return part1 + part2 + part3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUsersProcessed()
|
||||||
|
{
|
||||||
|
return usersProcessed.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUsersChanged()
|
||||||
|
{
|
||||||
|
return usersChanged.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getErrors()
|
||||||
|
{
|
||||||
|
return errors.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A scheduled job that checks and upgrades users passwords to the system's preferred encoding.
|
||||||
|
* <p>
|
||||||
|
* Job data:
|
||||||
|
* <ul>
|
||||||
|
* <li><b>upgradePasswordHashWorker</b> - The worker that performs the actual processing.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @see UpgradePasswordHashWorker
|
||||||
|
*/
|
||||||
|
public static class UpgradePasswordHashJob implements Job
|
||||||
|
{
|
||||||
|
public static final String JOB_DATA_WORKER = "upgradePasswordHashWorker";
|
||||||
|
|
||||||
|
public void execute(JobExecutionContext context) throws JobExecutionException
|
||||||
|
{
|
||||||
|
JobDataMap jobData = context.getJobDetail().getJobDataMap();
|
||||||
|
|
||||||
|
// extract the content Cleanup to use
|
||||||
|
Object upgradePasswordHashWorkerObj = jobData.get(JOB_DATA_WORKER);
|
||||||
|
if (upgradePasswordHashWorkerObj == null || !(upgradePasswordHashWorkerObj instanceof UpgradePasswordHashWorker))
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException(
|
||||||
|
"UpgradePasswordHashJob data '" + JOB_DATA_WORKER + "' must reference a " + UpgradePasswordHashWorker.class.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
UpgradePasswordHashWorker worker = (UpgradePasswordHashWorker)upgradePasswordHashWorkerObj;
|
||||||
|
worker.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.alfresco.repo.security.authentication;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.transaction.Status;
|
||||||
|
import javax.transaction.UserTransaction;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||||
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
|
||||||
|
import org.alfresco.service.ServiceRegistry;
|
||||||
|
import org.alfresco.test_category.OwnJVMTestsCategory;
|
||||||
|
import org.alfresco.util.ApplicationContextHelper;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Category(OwnJVMTestsCategory.class)
|
||||||
|
public class UpgradePasswordHashTest extends TestCase
|
||||||
|
{
|
||||||
|
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
|
||||||
|
|
||||||
|
private UserTransaction userTransaction;
|
||||||
|
private ServiceRegistry serviceRegistry;
|
||||||
|
private UpgradePasswordHashWorker upgradePasswordHashWorker;
|
||||||
|
private List<String> testUserNames;
|
||||||
|
|
||||||
|
public UpgradePasswordHashTest()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UpgradePasswordHashTest(String arg0)
|
||||||
|
{
|
||||||
|
super(arg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUp() throws Exception
|
||||||
|
{
|
||||||
|
if (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_NONE)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException(
|
||||||
|
"A previous tests did not clean up transaction: " +
|
||||||
|
AlfrescoTransactionSupport.getTransactionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceRegistry = (ServiceRegistry)ctx.getBean("ServiceRegistry");
|
||||||
|
|
||||||
|
upgradePasswordHashWorker = (UpgradePasswordHashWorker)ctx.getBean("upgradePasswordHashWorker");
|
||||||
|
|
||||||
|
userTransaction = serviceRegistry.getTransactionService().getUserTransaction();
|
||||||
|
userTransaction.begin();
|
||||||
|
|
||||||
|
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName());
|
||||||
|
|
||||||
|
createTestUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createTestUsers() throws Exception
|
||||||
|
{
|
||||||
|
// create 50 users and change their properties back to how
|
||||||
|
// they would have been pre-upgrade.
|
||||||
|
|
||||||
|
testUserNames = new ArrayList<String>(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void deleteTestUsers() throws Exception
|
||||||
|
{
|
||||||
|
// delete all the test users.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception
|
||||||
|
{
|
||||||
|
// remove all the test users we created
|
||||||
|
deleteTestUsers();
|
||||||
|
|
||||||
|
// cleanup transaction if necessary so we don't effect subsequent tests
|
||||||
|
if ((userTransaction.getStatus() == Status.STATUS_ACTIVE) || (userTransaction.getStatus() == Status.STATUS_MARKED_ROLLBACK))
|
||||||
|
{
|
||||||
|
userTransaction.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthenticationUtil.clearCurrentSecurityContext();
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWorkerWithDefaultConfiguration() throws Exception
|
||||||
|
{
|
||||||
|
// execute the worker to upgrade all users
|
||||||
|
this.upgradePasswordHashWorker.execute();
|
||||||
|
|
||||||
|
// ensure all the test users have been upgraded to use the preferred encoding
|
||||||
|
}
|
||||||
|
|
||||||
|
public void xxxtestWorkerWithLegacyConfiguration() throws Exception
|
||||||
|
{
|
||||||
|
// execute the worker to upgrade all users
|
||||||
|
this.upgradePasswordHashWorker.execute();
|
||||||
|
|
||||||
|
// ensure all the test users have been upgraded but maintain the MD4 encoding
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user