mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
Merged V3.4-BUG-FIX to HEAD
28650: Merged DEV/TEMPORARY to V3.4-BUG-FIX 28637: ALF-5601: WCM Reviewer should be able to modify 'Launch Date' of the review item. Set "wcmwf:launchDate" to read-only on "submitpendingTask". 28697: Fix for ALF-2711 - Fix to handle incorrect (negative size!) content length headers sent by Adobe Flash when uploading files over 2GB. 28702: Merged DEV to V3.4-BUG-FIX 28693: ALF-9314: Unable to add to multi-valued properties via AVM Console The node property value of Collection type must be set within square braces as a comma separated values without spaces. E.g. [aaa,bbb,ccc] 28718: Merged PATCHES/V3.4.2 to V3.4-BUG-FIX 28569: ALF-9253 / ALF-9166: 'A valid SecureContext was not provided in the RequestContext' exception on startup following upgrade to 3.4.1 28618: ALF-8385 / ALF-9364: Merged DEV/TEMPORARY to PATCHES/V3.4.2 28565: ALF-5887 Addition of RenameUser command line toolContext - PersonServiceImpl should not disable normal behaviour when handling duplicate Person NodeRefs as the userAuthorityCache does not get updated correctly - Tool (base class for Import, Export and RenameUser command line tools) should not automatically login if setLogin(false) has been called. 28719: Merged V3.4 to V3.4-BUG-FIX 28648: ALF-9103: Remove obsolete (and mis-spelled) use-old-dm-alcs-context.xml.sample 28701: Corrected library for - Fix for ALF-7860 - Regression: Close button doesn't work in Node Browser git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@28721 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -1,53 +0,0 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
|
||||
|
||||
<beans>
|
||||
|
||||
<bean id="dmPermissionsDaoComponent" class="org.alfresco.repo.domain.hibernate.OldADMPermissionsDaoComponentImpl" >
|
||||
<property name="aclDaoComponent">
|
||||
<ref bean="aclDaoComponent" />
|
||||
</property>
|
||||
<property name="protocolToACLDAO">
|
||||
<map>
|
||||
<entry key="workspace"><ref bean="nodeACLDAO"></ref></entry>
|
||||
<entry key="avm"><ref bean="avmACLDAO"/></entry>
|
||||
</map>
|
||||
</property>
|
||||
<property name="defaultACLDAO">
|
||||
<ref bean="nodeACLDAO"/>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="nodeACLDAO" class="org.alfresco.repo.domain.hibernate.NodeAccessControlListDAO">
|
||||
<property name="sessionFactory">
|
||||
<ref bean="sessionFactory" />
|
||||
</property>
|
||||
<property name="nodeDaoService">
|
||||
<ref bean="nodeDaoService" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="patch.updateDmPermissions" class="java.util.ArrayList" />
|
||||
|
||||
<bean id="aclDaoComponentImpl" class="org.alfresco.repo.domain.hibernate.AclDaoComponentImpl">
|
||||
<property name="sessionFactory">
|
||||
<ref bean="sessionFactory" />
|
||||
</property>
|
||||
<property name="qnameDAO">
|
||||
<ref bean="qnameDAO" />
|
||||
</property>
|
||||
<property name="patchDAO">
|
||||
<ref bean="patchDAO" />
|
||||
</property>
|
||||
<property name="avmNodeDAO">
|
||||
<ref bean="newAvmNodeDAO" />
|
||||
</property>
|
||||
<property name="aclCache">
|
||||
<ref bean="aclCache" />
|
||||
</property>
|
||||
<property name="useOldPermissions">
|
||||
<value>true</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
@@ -58,6 +58,7 @@
|
||||
<bean id="renditionDefinitionPersister" class="org.alfresco.repo.rendition.RenditionDefinitionPersisterImpl" >
|
||||
<property name="runtimeActionService" ref="actionService" />
|
||||
<property name="nodeService" ref="NodeService" />
|
||||
<property name="behaviourFilter" ref="policyBehaviourFilter" />
|
||||
</bean>
|
||||
|
||||
<bean id="renditionLocationResolver" class="org.alfresco.repo.rendition.StandardRenditionLocationResolverImpl" >
|
||||
|
@@ -958,7 +958,7 @@ public class AVMInterpreter
|
||||
|
||||
private static String[] getCSVArray(String valueString)
|
||||
{
|
||||
String[] elements = valueString.split(",\\s+");
|
||||
String[] elements = valueString.split(",");
|
||||
|
||||
if (elements.length == 0)
|
||||
{
|
||||
|
@@ -27,6 +27,7 @@ import java.util.Set;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.action.ActionModel;
|
||||
import org.alfresco.repo.action.RuntimeActionService;
|
||||
import org.alfresco.repo.policy.BehaviourFilter;
|
||||
import org.alfresco.service.cmr.action.Action;
|
||||
import org.alfresco.service.cmr.action.CompositeAction;
|
||||
import org.alfresco.service.cmr.rendition.RenditionDefinition;
|
||||
@@ -59,6 +60,7 @@ public class RenditionDefinitionPersisterImpl implements RenditionDefinitionPers
|
||||
/* Injected services */
|
||||
private NodeService nodeService;
|
||||
private RuntimeActionService runtimeActionService;
|
||||
private BehaviourFilter behaviourFilter;
|
||||
|
||||
/**
|
||||
* Injects the NodeService bean.
|
||||
@@ -80,6 +82,12 @@ public class RenditionDefinitionPersisterImpl implements RenditionDefinitionPers
|
||||
this.runtimeActionService = runtimeActionService;
|
||||
}
|
||||
|
||||
public void setBehaviourFilter(BehaviourFilter behaviourFilter)
|
||||
{
|
||||
this.behaviourFilter = behaviourFilter;
|
||||
}
|
||||
|
||||
|
||||
public List<RenditionDefinition> loadRenditionDefinitions()
|
||||
{
|
||||
checkRenderingActionRootNodeExists();
|
||||
@@ -148,12 +156,23 @@ public class RenditionDefinitionPersisterImpl implements RenditionDefinitionPers
|
||||
{
|
||||
NodeRef actionNodeRef = findOrCreateActionNode(renderingAction);
|
||||
|
||||
// ALF-9166 describes a problem whereby versionable saved rendition definition nodes cause problems on upgrade.
|
||||
// This appears to be due to a rule defined on Company Home. The behaviour suppression below is a workaround for that issue.
|
||||
try
|
||||
{
|
||||
behaviourFilter.disableBehaviour(actionNodeRef, ContentModel.ASPECT_VERSIONABLE);
|
||||
|
||||
// TODO Serialize using JSON content instead.
|
||||
// The current serialization mechanism creates a complex content model
|
||||
// structure which is verbose and a JSON-based approach using a simplified
|
||||
// content model perhaps could offer performance improvements.
|
||||
runtimeActionService.saveActionImpl(actionNodeRef, renderingAction);
|
||||
}
|
||||
finally
|
||||
{
|
||||
behaviourFilter.enableBehaviour(actionNodeRef, ContentModel.ASPECT_VERSIONABLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteRenditionDefinition(RenditionDefinition renderingAction)
|
||||
{
|
||||
|
@@ -44,7 +44,9 @@ import org.alfresco.repo.node.NodeServicePolicies;
|
||||
import org.alfresco.repo.policy.JavaBehaviour;
|
||||
import org.alfresco.repo.policy.PolicyComponent;
|
||||
import org.alfresco.repo.search.impl.lucene.AbstractLuceneQueryParser;
|
||||
import org.alfresco.repo.security.person.PersonServiceImpl;
|
||||
import org.alfresco.repo.tenant.TenantService;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
@@ -1108,7 +1110,7 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor
|
||||
String authAfter = DefaultTypeConverter.INSTANCE.convert(String.class, after.get(idProp));
|
||||
if (!EqualsHelper.nullSafeEquals(authBefore, authAfter))
|
||||
{
|
||||
if (authBefore.equalsIgnoreCase(authAfter))
|
||||
if (AlfrescoTransactionSupport.getResource(PersonServiceImpl.KEY_ALLOW_UID_UPDATE) != null || authBefore.equalsIgnoreCase(authAfter))
|
||||
{
|
||||
if (isAuthority)
|
||||
{
|
||||
|
@@ -120,7 +120,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
|
||||
private static final String SYSTEM_USAGE_WARN_LIMIT_USERS_EXCEEDED_VERBOSE = "system.usage.err.limit_users_exceeded_verbose";
|
||||
|
||||
private static final String KEY_POST_TXN_DUPLICATES = "PersonServiceImpl.KEY_POST_TXN_DUPLICATES";
|
||||
private static final String KEY_ALLOW_UID_UPDATE = "PersonServiceImpl.KEY_ALLOW_UID_UPDATE";
|
||||
public static final String KEY_ALLOW_UID_UPDATE = "PersonServiceImpl.KEY_ALLOW_UID_UPDATE";
|
||||
private static final String KEY_USERS_CREATED = "PersonServiceImpl.KEY_USERS_CREATED";
|
||||
|
||||
private StoreRef storeRef;
|
||||
@@ -603,10 +603,6 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
|
||||
{
|
||||
public Object execute() throws Throwable
|
||||
{
|
||||
try
|
||||
{
|
||||
policyBehaviourFilter.disableBehaviour(ContentModel.TYPE_PERSON);
|
||||
|
||||
if (duplicateMode.equalsIgnoreCase(SPLIT))
|
||||
{
|
||||
logger.info("Splitting " + postTxnDuplicates.size() + " duplicate person objects.");
|
||||
@@ -627,11 +623,6 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
|
||||
logger.debug("Duplicate person objects exist");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
policyBehaviourFilter.enableBehaviour(ContentModel.TYPE_PERSON);
|
||||
}
|
||||
|
||||
// Done
|
||||
return null;
|
||||
@@ -644,7 +635,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
|
||||
{
|
||||
for (NodeRef nodeRef : toDelete)
|
||||
{
|
||||
nodeService.deleteNode(nodeRef);
|
||||
deletePerson(nodeRef);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -58,6 +58,7 @@ import org.alfresco.service.cmr.security.PersonService;
|
||||
import org.alfresco.service.cmr.security.PersonService.PersonInfo;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.tools.RenameUser;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.alfresco.util.EqualsHelper;
|
||||
import org.alfresco.util.GUID;
|
||||
@@ -79,11 +80,6 @@ public class PersonTest extends TestCase
|
||||
private MutableAuthenticationDao authenticationDAO;
|
||||
private UserTransaction testTX;
|
||||
|
||||
public PersonTest()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName());
|
||||
@@ -1388,4 +1384,79 @@ public class PersonTest extends TestCase
|
||||
personService.createPerson(properties);
|
||||
personService.notifyPerson(userName, "abc");
|
||||
}
|
||||
|
||||
public void testRenameUser() throws Exception
|
||||
{
|
||||
// Note: RenameUserTest contains unit tests.
|
||||
|
||||
// End the Spring-managed txn
|
||||
testTX.commit();
|
||||
|
||||
final String username = AuthenticationUtil.getAdminUserName();
|
||||
|
||||
final String oldUsername = GUID.generate();
|
||||
final String newUsername = oldUsername+GUID.generate();
|
||||
|
||||
// Create a person
|
||||
final NodeRef person = transactionService.getRetryingTransactionHelper().doInTransaction(
|
||||
new RetryingTransactionCallback<NodeRef>()
|
||||
{
|
||||
public NodeRef execute() throws Throwable
|
||||
{
|
||||
// Tidy up failed runs
|
||||
if (personService.personExists(oldUsername))
|
||||
{
|
||||
personService.deletePerson(oldUsername);
|
||||
}
|
||||
if (personService.personExists(newUsername))
|
||||
{
|
||||
personService.deletePerson(newUsername);
|
||||
}
|
||||
|
||||
// Generate a person node
|
||||
Map<QName, Serializable> properties = createDefaultProperties(oldUsername, "firstName", "lastName", "email@orgId", "orgId", null);
|
||||
NodeRef person = personService.createPerson(properties);
|
||||
|
||||
// Check the person exists
|
||||
assertEquals(oldUsername, nodeService.getProperty(person, ContentModel.PROP_USERNAME));
|
||||
assertEquals(person, personService.getPerson(oldUsername));
|
||||
assertFalse("new user should not exist yet", personService.personExists(newUsername));
|
||||
return person;
|
||||
}
|
||||
}, false, true);
|
||||
|
||||
// Run the RenameUser cmd line tool
|
||||
// - override exit so we don't and assert normal exit
|
||||
// - Don't ask for a password as we may not know it in a test
|
||||
// - call start rather than main to get correct instance
|
||||
RenameUser renameUser = new RenameUser()
|
||||
{
|
||||
@Override
|
||||
protected void exit(int status)
|
||||
{
|
||||
assertEquals("Tool exit status should be normal", 0, status);
|
||||
}
|
||||
};
|
||||
renameUser.setLogin(false);
|
||||
renameUser.start(new String[] {"-user", username, oldUsername, newUsername});
|
||||
|
||||
// Check person has been renamed and the delete it.
|
||||
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>()
|
||||
{
|
||||
public Void execute() throws Throwable
|
||||
{
|
||||
String newUserName = (String) nodeService.getProperty(person, ContentModel.PROP_USERNAME);
|
||||
assertEquals(newUsername, newUserName);
|
||||
|
||||
// Check the person exists
|
||||
assertEquals(newUsername, nodeService.getProperty(person, ContentModel.PROP_USERNAME));
|
||||
assertEquals(person, personService.getPerson(newUsername));
|
||||
assertFalse("old user should no longer exist", personService.personExists(oldUsername));
|
||||
|
||||
// Get rid of the test person
|
||||
personService.deletePerson(newUsername);
|
||||
return null;
|
||||
}
|
||||
}, false, true);
|
||||
}
|
||||
}
|
||||
|
561
source/java/org/alfresco/tools/RenameUser.java
Normal file
561
source/java/org/alfresco/tools/RenameUser.java
Normal file
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2011 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.tools;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.IllegalCharsetNameException;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
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.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.repo.security.person.PersonServiceImpl;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.security.NoSuchPersonException;
|
||||
import org.alfresco.service.cmr.security.PersonService;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.util.VmShutdownListener;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Rename user tool. This tool provides minimal support for renaming users.
|
||||
* See {@link displayHelp} message for restrictions.
|
||||
* <pre>
|
||||
* Usage: renameUser -user username [options] oldUsername newUsername");
|
||||
* renameUser -user username [options] -file filename");
|
||||
* </pre>
|
||||
* The csv file has a simple comma separated list, with
|
||||
* a pair of usernames on each line. Comments and blank
|
||||
* lines may also be included. For example:
|
||||
* <pre>
|
||||
* # List of usernames to change
|
||||
*
|
||||
* # oldUsername,newUsername
|
||||
* johnp,ceo # President and CEO
|
||||
* johnn,cto # CTO and Chairman
|
||||
* </pre>
|
||||
*
|
||||
* @author Alan Davis
|
||||
*/
|
||||
public class RenameUser extends Tool
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(RenameUser.class);
|
||||
|
||||
/** User Rename Tool Context */
|
||||
protected RenameUserToolContext context;
|
||||
private boolean login = true;
|
||||
|
||||
PersonService personService;
|
||||
NodeService nodeService;
|
||||
|
||||
private PersonService getPersonService()
|
||||
{
|
||||
if (personService == null)
|
||||
{
|
||||
personService = getServiceRegistry().getPersonService();
|
||||
}
|
||||
return personService;
|
||||
}
|
||||
|
||||
private NodeService getNodeService()
|
||||
{
|
||||
if (nodeService == null)
|
||||
{
|
||||
nodeService = getServiceRegistry().getNodeService();
|
||||
}
|
||||
return nodeService;
|
||||
}
|
||||
|
||||
public void setLogin(boolean login)
|
||||
{
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry Point
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
Tool tool = new RenameUser();
|
||||
tool.start(args);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.alfresco.tools.Tool#processArgs(java.lang.String[])
|
||||
*/
|
||||
@Override
|
||||
protected ToolContext processArgs(String[] args)
|
||||
throws ToolArgumentException
|
||||
{
|
||||
context = new RenameUserToolContext();
|
||||
context.setLogin(login);
|
||||
|
||||
int i = 0;
|
||||
while (i < args.length)
|
||||
{
|
||||
if (args[i].equals("-h") || args[i].equals("-help"))
|
||||
{
|
||||
context.setHelp(true);
|
||||
break;
|
||||
}
|
||||
else if (args[i].equals("-user"))
|
||||
{
|
||||
i++;
|
||||
if (i == args.length || args[i].length() == 0)
|
||||
{
|
||||
throw new ToolArgumentException("The value <user> for the option -user must be specified");
|
||||
}
|
||||
context.setUsername(args[i]);
|
||||
}
|
||||
else if (args[i].equals("-pwd"))
|
||||
{
|
||||
i++;
|
||||
if (i == args.length || args[i].length() == 0)
|
||||
{
|
||||
throw new ToolArgumentException("The value <password> for the option -pwd must be specified");
|
||||
}
|
||||
context.setPassword(args[i]);
|
||||
}
|
||||
else if (args[i].equals("-encoding"))
|
||||
{
|
||||
i++;
|
||||
if (i == args.length || args[i].length() == 0)
|
||||
{
|
||||
throw new ToolArgumentException("The value <encoding> for the option -encoding must be specified");
|
||||
}
|
||||
try
|
||||
{
|
||||
context.encoding = Charset.forName(args[i]);
|
||||
}
|
||||
catch (IllegalCharsetNameException e)
|
||||
{
|
||||
throw new ToolArgumentException("The value <encoding> is not recognised");
|
||||
}
|
||||
catch (UnsupportedCharsetException e)
|
||||
{
|
||||
throw new ToolArgumentException("The value <encoding> is unsupported");
|
||||
}
|
||||
}
|
||||
else if (args[i].equals("-quiet"))
|
||||
{
|
||||
context.setQuiet(true);
|
||||
}
|
||||
else if (args[i].equals("-verbose"))
|
||||
{
|
||||
context.setVerbose(true);
|
||||
}
|
||||
else if (args[i].equals("-f") || args[i].equals("-file"))
|
||||
{
|
||||
i++;
|
||||
if (i == args.length || args[i].length() == 0)
|
||||
{
|
||||
throw new ToolArgumentException("The value <filename> for the option -file must be specified");
|
||||
}
|
||||
context.setFilename(args[i]);
|
||||
}
|
||||
else if (!args[i].startsWith("-"))
|
||||
{
|
||||
i++;
|
||||
if (i == args.length || args[i-1].trim().length() == 0 || args[i].trim().length() == 0)
|
||||
{
|
||||
throw new ToolArgumentException("Both <oldUsername> <newUsername> must be specified");
|
||||
}
|
||||
if (context.userCount() > 0)
|
||||
{
|
||||
throw new ToolArgumentException("Only one <oldUsername> <newUsername> pair may be " +
|
||||
"specified on the command line. See the -file option");
|
||||
}
|
||||
String oldUsername = args[i-1].trim();
|
||||
String newUsername = args[i].trim();
|
||||
String error = context.add(-1, null, oldUsername, newUsername);
|
||||
if (error != null)
|
||||
{
|
||||
throw new ToolArgumentException(error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ToolArgumentException("Unknown option " + args[i]);
|
||||
}
|
||||
|
||||
// next argument
|
||||
i++;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.tools.Tool#displayHelp()
|
||||
*/
|
||||
protected @Override
|
||||
/*package*/ void displayHelp()
|
||||
{
|
||||
logError("This tool provides minimal support for renaming users. It fixes");
|
||||
logError("authorities, group memberships and current zone (older versions");
|
||||
logError("still require a property change).");
|
||||
logError("");
|
||||
logError("WARNING: It does NOT change properties that store the username such");
|
||||
logError(" as (creator, modifier, lock owner or owner). Of these owner");
|
||||
logError(" and lock affect user rights. The username is also used");
|
||||
logError(" directly in workflow, for RM caveats, for Share invites and");
|
||||
logError(" auditing");
|
||||
logError("");
|
||||
logError("Usage: renameUser -user username [options] oldUsername newUsername");
|
||||
logError(" renameUser -user username [options] -file filename");
|
||||
logError("");
|
||||
logError(" username: username for login");
|
||||
logError("oldUsername: current username ");
|
||||
logError("newUsername: replacement username ");
|
||||
logError("");
|
||||
logError("Options:");
|
||||
logError(" -h[elp] display this help");
|
||||
logError(" -pwd password for login");
|
||||
logError(" -f[ile] csv file of old and new usernames");
|
||||
logError(" -encoding for source file (default: " + Charset.defaultCharset() + ")");
|
||||
logError(" -quiet do not display any messages during rename");
|
||||
logError(" -verbose report rename progress");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.alfresco.tools.Tool#getToolName()
|
||||
*/
|
||||
@Override
|
||||
protected String getToolName()
|
||||
{
|
||||
return "Alfresco Rename User";
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.alfresco.tools.Tool#execute()
|
||||
*/
|
||||
@Override
|
||||
protected int execute() throws ToolException
|
||||
{
|
||||
// Used for ability to be final and have a set
|
||||
final AtomicInteger status = new AtomicInteger(0);
|
||||
|
||||
BatchProcessWorker<User> worker = new BatchProcessWorkerAdaptor<User>()
|
||||
{
|
||||
public void process(final User user) throws Throwable
|
||||
{
|
||||
RunAsWork<Void> runAsWork = new RunAsWork<Void>()
|
||||
{
|
||||
@Override
|
||||
public Void doWork() throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
renameUser(user.getOldUsername(), user.getNewUsername());
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
status.set(handleError(t));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
AuthenticationUtil.runAs(runAsWork, context.getUsername());
|
||||
}
|
||||
};
|
||||
|
||||
// Use 2 threads, 20 User objects per transaction. Log every 100 entries.
|
||||
BatchProcessor<User> processor = new BatchProcessor<User>(
|
||||
"HomeFolderProviderSynchronizer",
|
||||
getServiceRegistry().getTransactionService().getRetryingTransactionHelper(),
|
||||
new WorkProvider(context),
|
||||
2, 20,
|
||||
null,
|
||||
logger, 100);
|
||||
processor.process(worker, true);
|
||||
|
||||
return status.get();
|
||||
}
|
||||
|
||||
private void renameUser(String oldUsername, String newUsername)
|
||||
{
|
||||
logInfo("\""+oldUsername+"\" --> \""+newUsername+"\"");
|
||||
try
|
||||
{
|
||||
NodeRef person = getPersonService().getPerson(oldUsername, false);
|
||||
|
||||
// Allow us to update the username just like the LDAP process
|
||||
AlfrescoTransactionSupport.bindResource(PersonServiceImpl.KEY_ALLOW_UID_UPDATE, Boolean.TRUE);
|
||||
|
||||
// Update the username property which will result in a PersonServiceImpl.onUpdateProperties call
|
||||
// on commit.
|
||||
getNodeService().setProperty(person, ContentModel.PROP_USERNAME, newUsername);
|
||||
}
|
||||
catch (NoSuchPersonException e)
|
||||
{
|
||||
logError("User does not exist: "+oldUsername);
|
||||
}
|
||||
}
|
||||
|
||||
public class User
|
||||
{
|
||||
private final String oldUsername;
|
||||
private final String newUsername;
|
||||
|
||||
public User(String oldUsername, String newUsername)
|
||||
{
|
||||
this.oldUsername = oldUsername;
|
||||
this.newUsername = newUsername;
|
||||
}
|
||||
|
||||
public String getOldUsername()
|
||||
{
|
||||
return oldUsername;
|
||||
}
|
||||
|
||||
public String getNewUsername()
|
||||
{
|
||||
return newUsername;
|
||||
}
|
||||
}
|
||||
|
||||
public class RenameUserToolContext extends ToolContext
|
||||
{
|
||||
/**
|
||||
* Old and new usernames to change.
|
||||
*/
|
||||
private List<User> usernames = new ArrayList<User>();
|
||||
|
||||
// Internal - used check the name has not been used before.
|
||||
private Set<String> uniqueNames = new HashSet<String>();
|
||||
|
||||
/**
|
||||
* Source filename of usernames.
|
||||
*/
|
||||
private String filename;
|
||||
|
||||
/**
|
||||
* Encoding of filename of usernames.
|
||||
*/
|
||||
private Charset encoding = Charset.defaultCharset();
|
||||
|
||||
public void setFilename(String filename)
|
||||
{
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
public String add(int lineNumber, String line, String oldUsername, String newUsername)
|
||||
{
|
||||
String error = null;
|
||||
if (oldUsername.equals(newUsername))
|
||||
{
|
||||
error = "Old and new usernames are the same";
|
||||
if (line != null)
|
||||
error = "Error on line " + lineNumber + " ("+error+"): " + line;
|
||||
}
|
||||
else if (uniqueNames.contains(oldUsername))
|
||||
{
|
||||
error = "Old username already specified";
|
||||
if (line != null)
|
||||
error = "Error on line " + lineNumber + " ("+error+"): " + line;
|
||||
}
|
||||
else if (uniqueNames.contains(newUsername))
|
||||
{
|
||||
error = "New username already specified";
|
||||
if (line != null)
|
||||
error = "Error on line " + lineNumber + " ("+error+"): " + line;
|
||||
}
|
||||
else
|
||||
{
|
||||
add(new User(oldUsername, newUsername));
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
private void add(User user)
|
||||
{
|
||||
usernames.add(user);
|
||||
uniqueNames.add(user.getOldUsername());
|
||||
uniqueNames.add(user.getNewUsername());
|
||||
}
|
||||
|
||||
public int userCount()
|
||||
{
|
||||
return usernames.size();
|
||||
}
|
||||
|
||||
public Iterator<User> iterator()
|
||||
{
|
||||
return usernames.iterator();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.alfresco.tools.ToolContext#validate()
|
||||
*/
|
||||
@Override
|
||||
/*package*/ void validate()
|
||||
{
|
||||
super.validate();
|
||||
|
||||
if (filename != null)
|
||||
{
|
||||
if (userCount() > 0)
|
||||
{
|
||||
throw new ToolArgumentException("<filename> should not have been specified if " +
|
||||
"<oldUsername> <newUsername> has been specified on the command line.");
|
||||
}
|
||||
File file = new File(filename);
|
||||
if (!file.exists())
|
||||
{
|
||||
throw new ToolArgumentException("File " + filename + " does not exist.");
|
||||
}
|
||||
if (!readFile(file))
|
||||
{
|
||||
throw new ToolArgumentException("File " + filename + " contained errors.");
|
||||
}
|
||||
}
|
||||
|
||||
if (userCount() == 0)
|
||||
{
|
||||
throw new ToolArgumentException("No old and new usernames have been specified.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the user names out of the file.
|
||||
* @param file to be read
|
||||
* @return {@code true} if there were no problems found with the file contents.
|
||||
*/
|
||||
private boolean readFile(File file)
|
||||
{
|
||||
BufferedReader in = null;
|
||||
boolean noErrors = true;
|
||||
try
|
||||
{
|
||||
in = new BufferedReader(new InputStreamReader(new FileInputStream(file), encoding.name()));
|
||||
int lineNumber = 1;
|
||||
for (String line = in.readLine(); line != null; line = in.readLine(), lineNumber++)
|
||||
{
|
||||
int i = line.indexOf('#');
|
||||
if (i != -1)
|
||||
{
|
||||
line = line.substring(0, i);
|
||||
}
|
||||
if (line.trim().length() != 0)
|
||||
{
|
||||
String[] names = line.split(",");
|
||||
String oldUsername = names[0].trim();
|
||||
String newUsername = names[1].trim();
|
||||
if (names.length != 2 || oldUsername.length() == 0 || newUsername.length() == 0)
|
||||
{
|
||||
RenameUser.this.logError("Error on line " + lineNumber + ": " + line);
|
||||
noErrors = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
String error = context.add(lineNumber, line, oldUsername, newUsername);
|
||||
if (error != null)
|
||||
{
|
||||
RenameUser.this.logError(error);
|
||||
noErrors = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ToolArgumentException("Failed to read <filename>.", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (in != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
in.close();
|
||||
} catch (IOException e)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
return noErrors;
|
||||
}
|
||||
}
|
||||
|
||||
// BatchProcessWorkProvider returns batches of 100 User objects.
|
||||
private class WorkProvider implements BatchProcessWorkProvider<User>
|
||||
{
|
||||
private static final int BATCH_SIZE = 100;
|
||||
|
||||
private final VmShutdownListener vmShutdownLister = new VmShutdownListener("getRenameUserWorkProvider");
|
||||
private final Iterator<User> iterator;
|
||||
private final int size;
|
||||
|
||||
public WorkProvider(RenameUserToolContext context)
|
||||
{
|
||||
iterator = context.iterator();
|
||||
size = context.userCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getTotalEstimatedWorkSize()
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Collection<User> getNextWork()
|
||||
{
|
||||
if (vmShutdownLister.isVmShuttingDown())
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Collection<User> results = new ArrayList<User>(BATCH_SIZE);
|
||||
while (results.size() < BATCH_SIZE && iterator.hasNext())
|
||||
{
|
||||
results.add(iterator.next());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
223
source/java/org/alfresco/tools/RenameUserTest.java
Normal file
223
source/java/org/alfresco/tools/RenameUserTest.java
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2011 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.tools;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.alfresco.tools.RenameUser.User;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit test for RenameUser. {@link PersonTest} contains integration tests.
|
||||
*
|
||||
* @author Alan Davis
|
||||
*/
|
||||
public class RenameUserTest
|
||||
{
|
||||
private String[] args;
|
||||
private File file;
|
||||
private RenameUser renameUser = new RenameUser();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
args = new String[6];
|
||||
args[0] = "-user";
|
||||
args[1] = "admin";
|
||||
args[2] = "-pwd";
|
||||
args[3] = "admin";
|
||||
args[4] = "oldUsername";
|
||||
args[5] = "newUsername";
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown()// throws Exception
|
||||
{
|
||||
if (file != null && file.exists())
|
||||
{
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private void createFile(String content) throws Exception
|
||||
{
|
||||
file = File.createTempFile("RenameUserTest", ".txt");
|
||||
args[4] = "-file";
|
||||
args[5] = file.getPath();
|
||||
|
||||
BufferedWriter out = new BufferedWriter(new FileWriter(file));
|
||||
out.write(content);
|
||||
out.close();
|
||||
}
|
||||
|
||||
private void processArgsAndValidate()
|
||||
{
|
||||
renameUser.processArgs(args);
|
||||
renameUser.context.validate();
|
||||
}
|
||||
|
||||
// Check that the expected (supplied) usernames are in the context
|
||||
private void assertUsers(String... usernames)
|
||||
{
|
||||
int length = usernames.length/2;
|
||||
assertEquals("Must have an even number of usernames passed to assertUsers", usernames.length, length*2);
|
||||
|
||||
assertEquals(length, renameUser.context.userCount());
|
||||
Iterator<User> iterator = renameUser.context.iterator();
|
||||
for (int i=0; i<length; i++)
|
||||
{
|
||||
User user = iterator.next();
|
||||
assertEquals(usernames[i*2], user.getOldUsername());
|
||||
assertEquals(usernames[i*2+1], user.getNewUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoginUsernameAndPassword() throws Exception
|
||||
{
|
||||
// Reset the password to be sure we are picking up the correct value.
|
||||
args[3] = "password";
|
||||
|
||||
renameUser.processArgs(args);
|
||||
|
||||
assertEquals("admin", renameUser.context.getUsername());
|
||||
assertEquals("password", renameUser.context.getPassword());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCmdLineUsernames() throws Exception
|
||||
{
|
||||
renameUser.processArgs(args);
|
||||
|
||||
assertUsers("oldUsername", "newUsername");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileUsernames() throws Exception
|
||||
{
|
||||
createFile("oldUsername1,newUsername1\n");
|
||||
processArgsAndValidate();
|
||||
|
||||
assertUsers("oldUsername1", "newUsername1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileUsernamesWithSpaces() throws Exception
|
||||
{
|
||||
createFile(" oldUsername1 , newUsername1 \n");
|
||||
processArgsAndValidate();
|
||||
|
||||
assertUsers("oldUsername1", "newUsername1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileMultipleUsernames() throws Exception
|
||||
{
|
||||
createFile("oldUsername1,newUsername1\n" +
|
||||
"oldUsername2,newUsername2\n");
|
||||
processArgsAndValidate();
|
||||
|
||||
assertUsers("oldUsername1", "newUsername1",
|
||||
"oldUsername2", "newUsername2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileNoNewlineAtEndOfFile() throws Exception
|
||||
{
|
||||
createFile("oldUsername1,newUsername1");
|
||||
processArgsAndValidate();
|
||||
|
||||
assertUsers("oldUsername1", "newUsername1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyLines() throws Exception
|
||||
{
|
||||
createFile("\n\noldUsername1,newUsername1\n\n");
|
||||
processArgsAndValidate();
|
||||
|
||||
assertUsers("oldUsername1", "newUsername1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComments() throws Exception
|
||||
{
|
||||
createFile("#A header comment\noldUsername1,newUsername1 #end of line comment\n\n");
|
||||
processArgsAndValidate();
|
||||
|
||||
assertUsers("oldUsername1", "newUsername1");
|
||||
}
|
||||
|
||||
@Test(expected=ToolArgumentException.class)
|
||||
public void testBadFilename() throws Exception
|
||||
{
|
||||
createFile("oldUsername1,newUsername1\n");
|
||||
args[5] = "rubbish.txt";
|
||||
processArgsAndValidate();
|
||||
}
|
||||
|
||||
@Test(expected=ToolArgumentException.class)
|
||||
public void testFileNoUsernames() throws Exception
|
||||
{
|
||||
createFile("#A comment\n");
|
||||
processArgsAndValidate();
|
||||
}
|
||||
|
||||
@Test(expected=ToolArgumentException.class)
|
||||
public void testTooManyUsernamesOnALine() throws Exception
|
||||
{
|
||||
createFile("\nname1,name2,name3\n");
|
||||
processArgsAndValidate();
|
||||
}
|
||||
|
||||
@Test(expected=ToolArgumentException.class)
|
||||
public void testDuplicateNewUsername() throws Exception
|
||||
{
|
||||
createFile("name1,name2\nname3,name1\n");
|
||||
processArgsAndValidate();
|
||||
}
|
||||
|
||||
@Test(expected=ToolArgumentException.class)
|
||||
public void testDuplicateOldUsername() throws Exception
|
||||
{
|
||||
createFile("name1,name2\nname2,name3\n");
|
||||
processArgsAndValidate();
|
||||
}
|
||||
|
||||
@Test(expected=ToolArgumentException.class)
|
||||
public void testSameUsernameCmdLine() throws Exception
|
||||
{
|
||||
args[5] = "oldUsername";
|
||||
processArgsAndValidate();
|
||||
}
|
||||
|
||||
@Test(expected=ToolArgumentException.class)
|
||||
public void testSameUsernameFile() throws Exception
|
||||
{
|
||||
createFile("name1,name1");
|
||||
processArgsAndValidate();
|
||||
}
|
||||
}
|
@@ -220,9 +220,15 @@ public abstract class Tool
|
||||
// Perform Tool behaviour
|
||||
logInfo(getToolName());
|
||||
initialiseRepository();
|
||||
if (toolContext.isLogin())
|
||||
{
|
||||
login();
|
||||
}
|
||||
long loginTime = System.nanoTime();
|
||||
if (toolContext.isLogin())
|
||||
{
|
||||
logInfo("Time to login "+((loginTime - startTime)/1000000000f)+" seconds");
|
||||
}
|
||||
status = execute();
|
||||
long executeTime = System.nanoTime();
|
||||
logInfo("Time to execute "+((executeTime - loginTime)/1000000000f)+" seconds");
|
||||
|
Reference in New Issue
Block a user