mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-30 18:15:39 +00:00
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
562 lines
19 KiB
Java
562 lines
19 KiB
Java
/*
|
|
* 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;
|
|
}
|
|
}
|
|
}
|