From 4443f4227955d87364a6095c61e001ffe038c3a4 Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Tue, 19 Dec 2006 15:04:47 +0000 Subject: [PATCH] Merged 1.4 to HEAD svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4380 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4386 . git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4659 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../authentication-services-context.xml | 17 + config/alfresco/cache-context.xml | 42 +++ .../repo/action/ActionServiceImpl.java | 7 +- .../admin/patch/util/ImportFileUpdater.java | 41 ++- .../org/alfresco/repo/cache/CacheTest.java | 9 + .../alfresco/repo/cache/EhCacheAdapter.java | 7 + .../org/alfresco/repo/cache/NullCache.java | 72 ++++ .../org/alfresco/repo/cache/SimpleCache.java | 3 + .../repo/cache/TransactionalCache.java | 35 ++ .../authentication/AuthenticationTest.java | 11 +- .../InMemoryTicketComponentImpl.java | 28 +- .../ldap/LDAPPersonExportSource.java | 38 +- .../security/person/PersonServiceImpl.java | 331 +++++++++++++++++- .../person/UIDBasedHomeFolderProvider.java | 6 + 14 files changed, 601 insertions(+), 46 deletions(-) create mode 100644 source/java/org/alfresco/repo/cache/NullCache.java diff --git a/config/alfresco/authentication-services-context.xml b/config/alfresco/authentication-services-context.xml index 104899d7bf..bdbe70fc9d 100644 --- a/config/alfresco/authentication-services-context.xml +++ b/config/alfresco/authentication-services-context.xml @@ -255,6 +255,20 @@ ${user.name.caseSensitive} + + + true + + + + SPLIT + + + true + + + false + @@ -342,6 +356,9 @@ + + + diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index a24976a99e..9943592d43 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -152,5 +152,47 @@ 5000 + + + + + + + + + + + + + + + ticketsCache + + + 1000 + + + false + + + + + + + + + + + + + + + + ticketsTransactionalCache + + + 10 + + \ No newline at end of file diff --git a/source/java/org/alfresco/repo/action/ActionServiceImpl.java b/source/java/org/alfresco/repo/action/ActionServiceImpl.java index 0ec6be6296..e81db779ec 100644 --- a/source/java/org/alfresco/repo/action/ActionServiceImpl.java +++ b/source/java/org/alfresco/repo/action/ActionServiceImpl.java @@ -475,11 +475,8 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A } catch (Throwable exception) { - // Log the exception - logger.error( - "An error was encountered whilst executing the action '" + action.getActionDefinitionName() + "'.", - exception); - + // DH: No logging of the exception. Leave the logging decision to the client code, + // which can handle the rethrown exception. if (executedAsynchronously == true) { // If one is specified, queue the compensating action ready for execution diff --git a/source/java/org/alfresco/repo/admin/patch/util/ImportFileUpdater.java b/source/java/org/alfresco/repo/admin/patch/util/ImportFileUpdater.java index 9d3c7bff99..5f30a2c328 100644 --- a/source/java/org/alfresco/repo/admin/patch/util/ImportFileUpdater.java +++ b/source/java/org/alfresco/repo/admin/patch/util/ImportFileUpdater.java @@ -3,9 +3,14 @@ */ package org.alfresco.repo.admin.patch.util; +import java.io.BufferedReader; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FileReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; @@ -34,6 +39,12 @@ public class ImportFileUpdater /** The destination export version number **/ private static String EXPORT_VERSION = "1.4.0"; + + /** Default encoding **/ + private static String DEFAULT_ENCODING = "UTF-8"; + + /** File encoding */ + private String fileEncoding = DEFAULT_ENCODING; /** Element names **/ private static String NAME_EXPORTER_VERSION = "exporterVersion"; @@ -42,6 +53,16 @@ public class ImportFileUpdater /** The current import version number **/ private String version; private boolean shownWarning = false; + + /** + * Set the file encoding. + * + * @param fileEncoding the file encoding + */ + public void setFileEncoding(String fileEncoding) + { + this.fileEncoding = fileEncoding; + } /** * Updates the passed import file into the equivalent 1.4 format. @@ -95,8 +116,11 @@ public class ImportFileUpdater XmlPullParserFactory factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); factory.setNamespaceAware(true); + InputStream inputStream = new FileInputStream(source); + Reader inputReader = new InputStreamReader(inputStream, this.fileEncoding); + XmlPullParser xpp = factory.newPullParser(); - xpp.setInput(new FileReader(source)); + xpp.setInput(new BufferedReader(inputReader)); return xpp; } @@ -108,6 +132,10 @@ public class ImportFileUpdater { throw new AlfrescoRuntimeException("The source file could not be loaded.", fileNotFound); } + catch (UnsupportedEncodingException exception) + { + throw new AlfrescoRuntimeException("Unsupported encoding", exception); + } } /** @@ -124,7 +152,7 @@ public class ImportFileUpdater OutputFormat format = OutputFormat.createPrettyPrint(); format.setNewLineAfterDeclaration(false); format.setIndentSize(INDENT_SIZE); - format.setEncoding("UTF-8"); + format.setEncoding(this.fileEncoding); return new XMLWriter(new FileOutputStream(destination), format); } @@ -573,11 +601,18 @@ public class ImportFileUpdater ImportFileUpdater util = new ImportFileUpdater(); util.updateImportFile(args[0], args[1]); } + else if (args.length == 3) + { + ImportFileUpdater util = new ImportFileUpdater(); + util.setFileEncoding(args[2]); + util.updateImportFile(args[0], args[1]); + } else { System.out.println(" ImportFileUpdater "); System.out.println(" source - 1.3 import file name to be updated"); System.out.println(" destination - name of the generated 1.4 import file"); + System.out.println(" file encoding (optional) - the file encoding, default is UTF-8"); } } diff --git a/source/java/org/alfresco/repo/cache/CacheTest.java b/source/java/org/alfresco/repo/cache/CacheTest.java index e85f3c43c0..4bdefef9af 100644 --- a/source/java/org/alfresco/repo/cache/CacheTest.java +++ b/source/java/org/alfresco/repo/cache/CacheTest.java @@ -17,6 +17,7 @@ package org.alfresco.repo.cache; import java.io.Serializable; +import java.util.Collection; import javax.transaction.Status; import javax.transaction.UserTransaction; @@ -86,6 +87,9 @@ public class CacheTest extends TestCase assertEquals("AAA", backingCache.get("A")); + Collection keys = backingCache.getKeys(); + assertEquals("Backing cache didn't return correct number of keys", 1, keys.size()); + backingCache.remove("A"); assertNull(backingCache.get("A")); } @@ -141,6 +145,11 @@ public class CacheTest extends TestCase assertEquals("Item not updated in txn cache", "XXX", transactionalCache.get(updatedTxnThree)); assertFalse("Item was put into backing cache", backingCache.contains(updatedTxnThree)); + // check that the keys collection is correct + Collection transactionalKeys = transactionalCache.getKeys(); + assertFalse("Transactionally removed item found in keys", transactionalKeys.contains(newGlobalOne)); + assertTrue("Transactionally added item not found in keys", transactionalKeys.contains(updatedTxnThree)); + // commit the transaction txn.commit(); diff --git a/source/java/org/alfresco/repo/cache/EhCacheAdapter.java b/source/java/org/alfresco/repo/cache/EhCacheAdapter.java index 5f2c37b9bf..85399c22a5 100644 --- a/source/java/org/alfresco/repo/cache/EhCacheAdapter.java +++ b/source/java/org/alfresco/repo/cache/EhCacheAdapter.java @@ -17,6 +17,7 @@ package org.alfresco.repo.cache; import java.io.Serializable; +import java.util.Collection; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheException; @@ -64,6 +65,12 @@ public class EhCacheAdapter } } + @SuppressWarnings("unchecked") + public Collection getKeys() + { + return cache.getKeys(); + } + @SuppressWarnings("unchecked") public V get(K key) { diff --git a/source/java/org/alfresco/repo/cache/NullCache.java b/source/java/org/alfresco/repo/cache/NullCache.java new file mode 100644 index 0000000000..2024b8a5e4 --- /dev/null +++ b/source/java/org/alfresco/repo/cache/NullCache.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.cache; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; + +/** + * A cache that does nothing - always. + *

+ * There are conditions under which code that expects to be caching, should not be. Using this + * cache, it becomes possible to configure a valid cache in whilst still ensuring that the + * actual caching is not performed. + * + * @author Derek Hulley + */ +public class NullCache implements SimpleCache +{ + public NullCache() + { + } + + /** NO-OP */ + public boolean contains(K key) + { + return false; + } + + public Collection getKeys() + { + return Collections.emptyList(); + } + + /** NO-OP */ + public V get(K key) + { + return null; + } + + /** NO-OP */ + public void put(K key, V value) + { + return; + } + + /** NO-OP */ + public void remove(K key) + { + return; + } + + /** NO-OP */ + public void clear() + { + return; + } +} diff --git a/source/java/org/alfresco/repo/cache/SimpleCache.java b/source/java/org/alfresco/repo/cache/SimpleCache.java index ef644120bd..30d91fe573 100644 --- a/source/java/org/alfresco/repo/cache/SimpleCache.java +++ b/source/java/org/alfresco/repo/cache/SimpleCache.java @@ -17,6 +17,7 @@ package org.alfresco.repo.cache; import java.io.Serializable; +import java.util.Collection; /** * Basic caching interface. @@ -32,6 +33,8 @@ public interface SimpleCache { public boolean contains(K key); + public Collection getKeys(); + public V get(K key); public void put(K key, V value); diff --git a/source/java/org/alfresco/repo/cache/TransactionalCache.java b/source/java/org/alfresco/repo/cache/TransactionalCache.java index f2080be2b4..208b57aab9 100644 --- a/source/java/org/alfresco/repo/cache/TransactionalCache.java +++ b/source/java/org/alfresco/repo/cache/TransactionalCache.java @@ -17,6 +17,8 @@ package org.alfresco.repo.cache; import java.io.Serializable; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import net.sf.ehcache.Cache; @@ -226,6 +228,39 @@ public class TransactionalCache return true; } } + + /** + * The keys returned are a union of the set of keys in the current transaction and + * those in the backing cache. + */ + @SuppressWarnings("unchecked") + public Collection getKeys() + { + Collection keys = null; + // in-txn layering + if (AlfrescoTransactionSupport.getTransactionId() != null) + { + keys = new HashSet(23); + TransactionData txnData = getTransactionData(); + if (!txnData.isClearOn) + { + // the backing cache is not due for a clear + Collection backingKeys = (Collection) sharedCache.getKeys(); + keys.addAll(backingKeys); + } + // add keys + keys.addAll((Collection) txnData.updatedItemsCache.getKeys()); + // remove keys + keys.removeAll((Collection) txnData.removedItemsCache.getKeys()); + } + else + { + // no transaction, so just use the backing cache + keys = (Collection) sharedCache.getKeys(); + } + // done + return keys; + } /** * Checks the per-transaction caches for the object before going to the shared cache. diff --git a/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java b/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java index e307338a4a..6a9c1b485a 100644 --- a/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java +++ b/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java @@ -39,6 +39,8 @@ import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; import net.sf.acegisecurity.providers.dao.SaltSource; import org.alfresco.model.ContentModel; +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl.Ticket; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.NodeRef; @@ -54,6 +56,7 @@ import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.ApplicationContextHelper; import org.springframework.context.ApplicationContext; +@SuppressWarnings("unchecked") public class AuthenticationTest extends TestCase { private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); @@ -81,6 +84,8 @@ public class AuthenticationTest extends TestCase private SaltSource saltSource; private TicketComponent ticketComponent; + + private SimpleCache ticketsCache; private AuthenticationService authenticationService; @@ -116,6 +121,7 @@ public class AuthenticationTest extends TestCase authenticationComponentImpl = (AuthenticationComponent) ctx.getBean("authenticationComponentImpl"); // permissionServiceSPI = (PermissionServiceSPI) // ctx.getBean("permissionService"); + ticketsCache = (SimpleCache) ctx.getBean("ticketsCache"); dao = (MutableAuthenticationDao) ctx.getBean("alfDaoImpl"); authenticationManager = (AuthenticationManager) ctx.getBean("authenticationManager"); @@ -143,7 +149,6 @@ public class AuthenticationTest extends TestCase deleteAndy(); authenticationComponent.clearCurrentSecurityContext(); - } private void deleteAndy() @@ -476,6 +481,7 @@ public class AuthenticationTest extends TestCase tc.setOneOff(false); tc.setTicketsExpire(false); tc.setValidDuration("P0D"); + tc.setTicketsCache(ticketsCache); dao.createUser("Andy", "ticket".toCharArray()); @@ -499,6 +505,7 @@ public class AuthenticationTest extends TestCase tc.setOneOff(true); tc.setTicketsExpire(false); tc.setValidDuration("P0D"); + tc.setTicketsCache(ticketsCache); dao.createUser("Andy", "ticket".toCharArray()); @@ -530,6 +537,7 @@ public class AuthenticationTest extends TestCase tc.setOneOff(false); tc.setTicketsExpire(true); tc.setValidDuration("P5S"); + tc.setTicketsCache(ticketsCache); dao.createUser("Andy", "ticket".toCharArray()); @@ -619,6 +627,7 @@ public class AuthenticationTest extends TestCase tc.setOneOff(false); tc.setTicketsExpire(true); tc.setValidDuration("P1D"); + tc.setTicketsCache(ticketsCache); dao.createUser("Andy", "ticket".toCharArray()); diff --git a/source/java/org/alfresco/repo/security/authentication/InMemoryTicketComponentImpl.java b/source/java/org/alfresco/repo/security/authentication/InMemoryTicketComponentImpl.java index db55b630e8..0451b6bf09 100644 --- a/source/java/org/alfresco/repo/security/authentication/InMemoryTicketComponentImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/InMemoryTicketComponentImpl.java @@ -16,11 +16,12 @@ */ package org.alfresco.repo.security.authentication; +import java.io.Serializable; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; import java.util.Set; +import org.alfresco.repo.cache.SimpleCache; import org.alfresco.service.cmr.repository.datatype.Duration; import org.alfresco.util.GUID; public class InMemoryTicketComponentImpl implements TicketComponent @@ -33,13 +34,18 @@ public class InMemoryTicketComponentImpl implements TicketComponent private boolean oneOff; - private HashMap tickets = new HashMap(); + private SimpleCache ticketsCache; // Can't use Ticket as it's private public InMemoryTicketComponentImpl() { super(); } + public void setTicketsCache(SimpleCache ticketsCache) + { + this.ticketsCache = ticketsCache; + } + public String getTicket(String userName) throws AuthenticationException { Date expiryDate = null; @@ -48,7 +54,7 @@ public class InMemoryTicketComponentImpl implements TicketComponent expiryDate = Duration.add(new Date(), validDuration); } Ticket ticket = new Ticket(ticketsExpire, expiryDate, userName); - tickets.put(ticket.getTicketId(), ticket); + ticketsCache.put(ticket.getTicketId(), ticket); return GRANTED_AUTHORITY_TICKET_PREFIX + ticket.getTicketId(); } @@ -61,7 +67,7 @@ public class InMemoryTicketComponentImpl implements TicketComponent } String key = ticketString.substring(GRANTED_AUTHORITY_TICKET_PREFIX.length()); - Ticket ticket = tickets.get(key); + Ticket ticket = ticketsCache.get(key); if (ticket == null) { throw new AuthenticationException("Missing ticket for " + ticketString); @@ -74,7 +80,7 @@ public class InMemoryTicketComponentImpl implements TicketComponent // TODO: Strengthen ticket as GUID is predicatble if(oneOff) { - tickets.remove(key); + ticketsCache.remove(key); } return ticket.getUserName(); } @@ -82,16 +88,16 @@ public class InMemoryTicketComponentImpl implements TicketComponent public void invalidateTicketById(String ticketString) { String key = ticketString.substring(GRANTED_AUTHORITY_TICKET_PREFIX.length()); - tickets.remove(key); + ticketsCache.remove(key); } public void invalidateTicketByUser(String userName) { Set toRemove = new HashSet(); - for(String key: tickets.keySet()) + for(String key: ticketsCache.getKeys()) { - Ticket ticket = tickets.get(key); + Ticket ticket = ticketsCache.get(key); if(ticket.getUserName().equals(userName)) { toRemove.add(ticket.getTicketId()); @@ -100,14 +106,16 @@ public class InMemoryTicketComponentImpl implements TicketComponent for(String id: toRemove) { - tickets.remove(id); + ticketsCache.remove(id); } } - private static class Ticket + public static class Ticket implements Serializable { + private static final long serialVersionUID = -5904510560161261049L; + private boolean expires; private Date expiryDate; diff --git a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPPersonExportSource.java b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPPersonExportSource.java index 1b05e495b3..793a09fa62 100644 --- a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPPersonExportSource.java +++ b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPPersonExportSource.java @@ -21,6 +21,7 @@ import java.io.File; import java.io.FileWriter; import java.io.Writer; import java.util.Collection; +import java.util.HashSet; import java.util.Map; import javax.naming.NamingEnumeration; @@ -67,7 +68,7 @@ public class LDAPPersonExportSource implements ExportSource private NamespaceService namespaceService; private Map attributeDefaults; - + private boolean errorOnMissingUID; public LDAPPersonExportSource() @@ -119,7 +120,7 @@ public class LDAPPersonExportSource implements ExportSource { this.errorOnMissingUID = errorOnMissingUID; } - + public void generateExport(XMLWriter writer) { QName nodeUUID = QName.createQName("sys:node-uuid", namespaceService); @@ -147,6 +148,8 @@ public class LDAPPersonExportSource implements ExportSource writer.startElement(NamespaceService.REPOSITORY_VIEW_PREFIX, "view", NamespaceService.REPOSITORY_VIEW_PREFIX + ":" + "view", new AttributesImpl()); + HashSet uids = new HashSet(); + InitialDirContext ctx = null; try { @@ -157,35 +160,44 @@ public class LDAPPersonExportSource implements ExportSource SearchControls userSearchCtls = new SearchControls(); userSearchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); - + userSearchCtls.setCountLimit(Integer.MAX_VALUE); NamingEnumeration searchResults = ctx.search(searchBase, personQuery, userSearchCtls); - while (searchResults.hasMoreElements()) + RESULT_LOOP: while (searchResults.hasMoreElements()) { SearchResult result = (SearchResult) searchResults.next(); Attributes attributes = result.getAttributes(); Attribute uidAttribute = attributes.get(userIdAttributeName); if (uidAttribute == null) { - if(errorOnMissingUID) + if (errorOnMissingUID) { - throw new ExportSourceImporterException( - "User returned by user search does not have mandatory user id attribute " + attributes); + throw new ExportSourceImporterException( + "User returned by user search does not have mandatory user id attribute " + + attributes); } else { - s_logger.warn("User returned by user search does not have mandatory user id attribute " + attributes); - continue; + s_logger.warn("User returned by user search does not have mandatory user id attribute " + + attributes); + continue RESULT_LOOP; } } String uid = (String) uidAttribute.get(0); + if (uids.contains(uid)) + { + s_logger.warn("Duplicate uid found - there will be more than one person object for this user - " + + uid); + } + + uids.add(uid); + if (s_logger.isDebugEnabled()) { s_logger.debug("Adding user for " + uid); } - writer.startElement(ContentModel.TYPE_PERSON.getNamespaceURI(), ContentModel.TYPE_PERSON .getLocalName(), ContentModel.TYPE_PERSON.toPrefixString(namespaceService), attrs); @@ -234,7 +246,7 @@ public class LDAPPersonExportSource implements ExportSource else { String defaultValue = attributeDefaults.get(key); - if(defaultValue != null) + if (defaultValue != null) { writer.characters(defaultValue.toCharArray(), 0, defaultValue.length()); } @@ -243,7 +255,7 @@ public class LDAPPersonExportSource implements ExportSource else { String defaultValue = attributeDefaults.get(key); - if(defaultValue != null) + if (defaultValue != null) { writer.characters(defaultValue.toCharArray(), 0, defaultValue.length()); } @@ -316,7 +328,7 @@ public class LDAPPersonExportSource implements ExportSource TransactionService txs = (TransactionService) ctx.getBean("transactionComponent"); UserTransaction tx = txs.getUserTransaction(); tx.begin(); - + File file = new File(args[0]); Writer writer = new BufferedWriter(new FileWriter(file)); XMLWriter xmlWriter = createXMLExporter(writer); diff --git a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java index 74d656bf1a..310ea8681b 100644 --- a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java +++ b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java @@ -27,6 +27,7 @@ import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.ldap.LDAPPersonExportSource; import org.alfresco.repo.security.permissions.PermissionServiceSPI; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -41,9 +42,20 @@ import org.alfresco.service.cmr.security.NoSuchPersonException; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; public class PersonServiceImpl implements PersonService { + private static Log s_logger = LogFactory.getLog(PersonServiceImpl.class); + + private static final String DELETE = "DELETE"; + + private static final String SPLIT = "SPLIT"; + + private static final String LEAVE = "LEAVE"; + public static final String SYSTEM_FOLDER = "/sys:system"; public static final String PEOPLE_FOLDER = SYSTEM_FOLDER + "/sys:people"; @@ -67,9 +79,17 @@ public class PersonServiceImpl implements PersonService private static Set mutableProperties; private boolean userNamesAreCaseSensitive = false; - + private String defaultHomeFolderProvider; + private boolean processDuplicates = true; + + private String duplicateMode = LEAVE; + + private boolean lastIsBest = true; + + private boolean includeAutoCreated = false; + static { Set props = new HashSet(); @@ -96,12 +116,32 @@ public class PersonServiceImpl implements PersonService { this.userNamesAreCaseSensitive = userNamesAreCaseSensitive; } - + void setDefaultHomeFolderProvider(String defaultHomeFolderProvider) { this.defaultHomeFolderProvider = defaultHomeFolderProvider; } + public void setDuplicateMode(String duplicateMode) + { + this.duplicateMode = duplicateMode; + } + + public void setIncludeAutoCreated(boolean includeAutoCreated) + { + this.includeAutoCreated = includeAutoCreated; + } + + public void setLastIsBest(boolean lastIsBest) + { + this.lastIsBest = lastIsBest; + } + + public void setProcessDuplicates(boolean processDuplicates) + { + this.processDuplicates = processDuplicates; + } + public NodeRef getPerson(String userName) { NodeRef personNode = getPersonOrNull(userName); @@ -130,21 +170,23 @@ public class PersonServiceImpl implements PersonService public NodeRef getPersonOrNull(String searchUserName) { + NodeRef returnRef = null; + SearchParameters sp = new SearchParameters(); sp.setLanguage(SearchService.LANGUAGE_LUCENE); - sp.setQuery("TYPE:\\{http\\://www.alfresco.org/model/content/1.0\\}person +@cm\\:userName:\"" + searchUserName - + "\""); + sp.setQuery("TYPE:\\{http\\://www.alfresco.org/model/content/1.0\\}person +@cm\\:userName:\"" + + searchUserName + "\""); sp.addStore(storeRef); sp.excludeDataInTheCurrentTransaction(false); ResultSet rs = null; + boolean singleton = true; + try { rs = searchService.query(sp); - NodeRef returnRef = null; - for (ResultSetRow row : rs) { @@ -164,8 +206,8 @@ public class PersonServiceImpl implements PersonService } else { - throw new AlfrescoRuntimeException("Found more than one user for " + searchUserName - + " (case sensitive)"); + singleton = false; + break; } } } @@ -179,15 +221,13 @@ public class PersonServiceImpl implements PersonService } else { - throw new AlfrescoRuntimeException("Found more than one user for " + searchUserName - + " (case insensitive)"); + singleton = false; + break; } } } } } - - return returnRef; } finally { @@ -196,6 +236,269 @@ public class PersonServiceImpl implements PersonService rs.close(); } } + + if (singleton) + { + return returnRef; + } + else + { + return handleDuplicates(searchUserName); + } + } + + private NodeRef handleDuplicates(String searchUserName) + { + if (processDuplicates) + { + NodeRef best = findBest(searchUserName); + if (duplicateMode.equalsIgnoreCase(SPLIT)) + { + split(searchUserName, best); + s_logger.info("Split duplicate person objects for uid " + searchUserName); + } + else if (duplicateMode.equalsIgnoreCase(DELETE)) + { + delete(searchUserName, best); + s_logger.info("Deleted duplicate person objects for uid " + searchUserName); + } + else + { + if (s_logger.isDebugEnabled()) + { + s_logger.debug("Duplicate person objects exist for uid " + searchUserName); + } + } + return best; + } + else + { + if (userNamesAreCaseSensitive) + { + throw new AlfrescoRuntimeException("Found more than one user for " + + searchUserName + " (case sensitive)"); + } + else + { + throw new AlfrescoRuntimeException("Found more than one user for " + + searchUserName + " (case insensitive)"); + } + } + } + + private void delete(String searchUserName, NodeRef best) + { + SearchParameters sp = new SearchParameters(); + sp.setLanguage(SearchService.LANGUAGE_LUCENE); + sp.setQuery("TYPE:\\{http\\://www.alfresco.org/model/content/1.0\\}person +@cm\\:userName:\"" + + searchUserName + "\""); + sp.addStore(storeRef); + sp.excludeDataInTheCurrentTransaction(false); + + ResultSet rs = null; + + try + { + rs = searchService.query(sp); + + for (ResultSetRow row : rs) + { + NodeRef nodeRef = row.getNodeRef(); + // Do not delete the best + if ((!best.equals(nodeRef)) && (nodeService.exists(nodeRef))) + { + String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty( + nodeRef, ContentModel.PROP_USERNAME)); + + if (userNamesAreCaseSensitive) + { + if (realUserName.equals(searchUserName)) + { + nodeService.deleteNode(nodeRef); + } + } + else + { + if (realUserName.equalsIgnoreCase(searchUserName)) + { + nodeService.deleteNode(nodeRef); + } + } + } + } + } + finally + { + if (rs != null) + { + rs.close(); + } + } + + } + + private void split(String searchUserName, NodeRef best) + { + SearchParameters sp = new SearchParameters(); + sp.setLanguage(SearchService.LANGUAGE_LUCENE); + sp.setQuery("TYPE:\\{http\\://www.alfresco.org/model/content/1.0\\}person +@cm\\:userName:\"" + + searchUserName + "\""); + sp.addStore(storeRef); + sp.excludeDataInTheCurrentTransaction(false); + + ResultSet rs = null; + + try + { + rs = searchService.query(sp); + + for (ResultSetRow row : rs) + { + NodeRef nodeRef = row.getNodeRef(); + // Do not delete the best + if ((!best.equals(nodeRef)) && (nodeService.exists(nodeRef))) + { + String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty( + nodeRef, ContentModel.PROP_USERNAME)); + + if (userNamesAreCaseSensitive) + { + if (realUserName.equals(searchUserName)) + { + nodeService.setProperty(nodeRef, ContentModel.PROP_USERNAME, searchUserName + + "(" + GUID.generate() + ")"); + } + } + else + { + if (realUserName.equalsIgnoreCase(searchUserName)) + { + nodeService.setProperty(nodeRef, ContentModel.PROP_USERNAME, searchUserName + + GUID.generate()); + } + } + } + } + } + finally + { + if (rs != null) + { + rs.close(); + } + } + + } + + private NodeRef findBest(String searchUserName) + { + SearchParameters sp = new SearchParameters(); + sp.setLanguage(SearchService.LANGUAGE_LUCENE); + sp.setQuery("TYPE:\\{http\\://www.alfresco.org/model/content/1.0\\}person +@cm\\:userName:\"" + + searchUserName + "\""); + sp.addStore(storeRef); + sp.excludeDataInTheCurrentTransaction(false); + if (lastIsBest) + { + sp.addSort(SearchParameters.SORT_IN_DOCUMENT_ORDER_DESCENDING); + } + else + { + sp.addSort(SearchParameters.SORT_IN_DOCUMENT_ORDER_ASCENDING); + } + + ResultSet rs = null; + + NodeRef fallBack = null; + + try + { + rs = searchService.query(sp); + + for (ResultSetRow row : rs) + { + NodeRef nodeRef = row.getNodeRef(); + if (fallBack == null) + { + fallBack = nodeRef; + } + // Do not delete the best + if (nodeService.exists(nodeRef)) + { + String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty( + nodeRef, ContentModel.PROP_USERNAME)); + + if (userNamesAreCaseSensitive) + { + if (realUserName.equals(searchUserName)) + { + if (includeAutoCreated || !wasAutoCreated(nodeRef, searchUserName)) + { + return nodeRef; + } + } + } + else + { + if (realUserName.equalsIgnoreCase(searchUserName)) + { + if (includeAutoCreated || !wasAutoCreated(nodeRef, searchUserName)) + { + return nodeRef; + } + } + } + } + } + } + finally + { + if (rs != null) + { + rs.close(); + } + } + return fallBack; + } + + private boolean wasAutoCreated(NodeRef nodeRef, String userName) + { + String testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, + ContentModel.PROP_FIRSTNAME)); + if ((testString == null) || !testString.equals(userName)) + { + return false; + } + + testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, + ContentModel.PROP_LASTNAME)); + if ((testString == null) || !testString.equals("")) + { + return false; + } + + testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, + ContentModel.PROP_EMAIL)); + if ((testString == null) || !testString.equals("")) + { + return false; + } + + testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, + ContentModel.PROP_ORGID)); + if ((testString == null) || !testString.equals("")) + { + return false; + } + + testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, + ContentModel.PROP_HOME_FOLDER_PROVIDER)); + if ((testString == null) || !testString.equals(defaultHomeFolderProvider)) + { + return false; + } + + return true; } public boolean createMissingPeople() @@ -225,8 +528,8 @@ public class PersonServiceImpl implements PersonService } else { - String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(personNode, - ContentModel.PROP_USERNAME)); + String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty( + personNode, ContentModel.PROP_USERNAME)); properties.put(ContentModel.PROP_USERNAME, realUserName); } diff --git a/source/java/org/alfresco/repo/security/person/UIDBasedHomeFolderProvider.java b/source/java/org/alfresco/repo/security/person/UIDBasedHomeFolderProvider.java index 1208023a48..87937f6a02 100644 --- a/source/java/org/alfresco/repo/security/person/UIDBasedHomeFolderProvider.java +++ b/source/java/org/alfresco/repo/security/person/UIDBasedHomeFolderProvider.java @@ -53,6 +53,12 @@ public class UIDBasedHomeFolderProvider extends ExistingPathBasedHomeFolderProvi { String uid = DefaultTypeConverter.INSTANCE.convert(String.class, getServiceRegistry().getNodeService() .getProperty(person, ContentModel.PROP_USERNAME)); + + if((uid == null) || (uid.length() == 0)) + { + throw new PersonException("Can not create a home space when the uid is null or empty"); + } + FileInfo fileInfo; // Test if it already exists