diff --git a/config/alfresco/authentication-services-context.xml b/config/alfresco/authentication-services-context.xml index 6a5c4fb0ca..fe8bfc971a 100644 --- a/config/alfresco/authentication-services-context.xml +++ b/config/alfresco/authentication-services-context.xml @@ -255,12 +255,8 @@ ${server.transaction.allow-writes} - - - - - - ${user.name.caseSensitive} + + ${user.name.caseSensitive} diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index 0081b9874c..27268967be 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -684,7 +684,6 @@ org.alfresco.service.cmr.security.PersonService.deletePerson=ACL_METHOD.ROLE_ADMINISTRATOR org.alfresco.service.cmr.security.PersonService.getAllPeople=ACL_ALLOW org.alfresco.service.cmr.security.PersonService.getPeopleContainer=ACL_ALLOW - org.alfresco.service.cmr.security.PersonService.getUserNamesAreCaseSensitive=ACL_ALLOW diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 736fe5d4ee..47d1746087 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -105,6 +105,16 @@ system.system_container.childname=sys:system system.people_container.childname=sys:people # Are user names case sensitive? +# ============================== +# +# NOTE: If you are using mysql you must have case sensitive collation +# +# You can do this when creating the alfresco database at the start +# CREATE DATABASE alfresco CHARACTER SET utf8 COLLATION utf8_bin; +# If you want to do this later this is a dump and load fix as it is dome when the database, tables and columns are created. +# +# Must other databases are case sensitive by default. +# user.name.caseSensitive=false diff --git a/source/java/org/alfresco/filesys/server/auth/ntlm/AlfrescoAuthenticator.java b/source/java/org/alfresco/filesys/server/auth/ntlm/AlfrescoAuthenticator.java index 9a65f8fea6..5b77d4171d 100644 --- a/source/java/org/alfresco/filesys/server/auth/ntlm/AlfrescoAuthenticator.java +++ b/source/java/org/alfresco/filesys/server/auth/ntlm/AlfrescoAuthenticator.java @@ -20,6 +20,7 @@ import java.security.NoSuchAlgorithmException; import net.sf.acegisecurity.Authentication; import org.alfresco.filesys.server.SrvSession; +import org.alfresco.filesys.server.auth.AuthContext; import org.alfresco.filesys.server.auth.CifsAuthenticator; import org.alfresco.filesys.server.auth.ClientInfo; import org.alfresco.filesys.server.auth.NTLanManAuthContext; @@ -182,26 +183,22 @@ public class AlfrescoAuthenticator extends CifsAuthenticator } /** - * Generate a challenge key + * Return an authentication context for the new session * - * @param sess SrvSession - * @return byte[] + * @return AuthContext */ - public byte[] getChallengeKey(SrvSession sess) + public AuthContext getAuthContext( SMBSrvSession sess) { - // In MD4 mode we generate the challenge locally - - byte[] key = null; - // Check if the client is already authenticated, and it is not a null logon - + + AuthContext authCtx = null; + if ( sess.hasAuthenticationContext() && sess.hasAuthenticationToken() && sess.getClientInformation().getLogonType() != ClientInfo.LogonNull) { // Return the previous challenge, user is already authenticated - NTLanManAuthContext authCtx = (NTLanManAuthContext) sess.getAuthenticationContext(); - key = authCtx.getChallenge(); + authCtx = (NTLanManAuthContext) sess.getAuthenticationContext(); // DEBUG @@ -210,11 +207,10 @@ public class AlfrescoAuthenticator extends CifsAuthenticator } else if ( m_authComponent.getNTLMMode() == NTLMMode.MD4_PROVIDER) { - // Generate a new challenge key, pack the key and return - - key = new byte[8]; - - DataPacker.putIntelLong(m_random.nextLong(), key, 0); + // Create a new authentication context for the session + + authCtx = new NTLanManAuthContext(); + sess.setAuthenticationContext( authCtx); } else { @@ -233,14 +229,17 @@ public class AlfrescoAuthenticator extends CifsAuthenticator // Get the challenge from the token if ( authToken.getChallenge() != null) - key = authToken.getChallenge().getBytes(); + { + authCtx = new NTLanManAuthContext( authToken.getChallenge().getBytes()); + sess.setAuthenticationContext( authCtx); + } } - // Return the challenge + // Return the authentication context - return key; + return authCtx; } - + /** * Perform MD4 user authentication * diff --git a/source/java/org/alfresco/filesys/server/auth/passthru/PassthruAuthenticator.java b/source/java/org/alfresco/filesys/server/auth/passthru/PassthruAuthenticator.java index 634c464b77..b2a83d1454 100644 --- a/source/java/org/alfresco/filesys/server/auth/passthru/PassthruAuthenticator.java +++ b/source/java/org/alfresco/filesys/server/auth/passthru/PassthruAuthenticator.java @@ -236,10 +236,8 @@ public class PassthruAuthenticator extends CifsAuthenticator implements SessionL } else { - // Set using the user name, lowercase the name if the person service is case insensitive + // Set using the user name - if ( m_personService.getUserNamesAreCaseSensitive() == false) - username = username.toLowerCase(); m_authComponent.setCurrentUser( username); // DEBUG diff --git a/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java index 0d57ee05c3..45ea54225e 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java @@ -19,7 +19,9 @@ package org.alfresco.filesys.smb.server.repo; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URL; +import java.net.URLDecoder; import java.util.List; import javax.transaction.UserTransaction; @@ -352,7 +354,22 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface URL appURL = this.getClass().getClassLoader().getResource(appPath.getValue()); if ( appURL == null) throw new DeviceContextException("Failed to find drag and drop application, " + appPath.getValue()); - File appFile = new File(appURL.getFile()); + + // Decode the URL path, it might contain escaped characters + + String appURLPath = null; + try + { + appURLPath = URLDecoder.decode( appURL.getFile(), "UTF-8"); + } + catch ( UnsupportedEncodingException ex) + { + throw new DeviceContextException("Failed to decode drag/drop path, " + ex.getMessage()); + } + + // Check that the drag/drop file exists + + File appFile = new File(appURLPath); if ( appFile.exists() == false) throw new DeviceContextException("Drag and drop application not found, " + appPath.getValue()); diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java index e9b02fada2..c8f4edc937 100644 --- a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java +++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java @@ -146,8 +146,8 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest TestWithUserUtils.authenticateUser(this.userName, PWD, this.rootNodeRef, this.authenticationService); this.userNodeRef = TestWithUserUtils.getCurrentUser(this.authenticationService); - permissionService.setPermission(this.rootNodeRef, this.userName.toLowerCase(), PermissionService.ALL_PERMISSIONS, true); - permissionService.setPermission(this.nodeRef, this.userName.toLowerCase(), PermissionService.ALL_PERMISSIONS, true); + permissionService.setPermission(this.rootNodeRef, this.userName, PermissionService.ALL_PERMISSIONS, true); + permissionService.setPermission(this.nodeRef, this.userName, PermissionService.ALL_PERMISSIONS, true); } /** diff --git a/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java b/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java index 92aa77577b..833885bda5 100644 --- a/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java +++ b/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java @@ -44,6 +44,7 @@ import org.alfresco.service.transaction.TransactionService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -58,7 +59,7 @@ import org.springframework.core.io.Resource; * * @author David Caruana */ -public class DescriptorServiceImpl implements DescriptorService, ApplicationListener, InitializingBean, ApplicationContextAware +public class DescriptorServiceImpl implements DescriptorService, ApplicationListener, InitializingBean, ApplicationContextAware, DisposableBean { private static Log logger = LogFactory.getLog(DescriptorServiceImpl.class); @@ -201,6 +202,13 @@ public class DescriptorServiceImpl implements DescriptorService, ApplicationList serverDescriptor = createServerDescriptor(); } + /** + * Destruction hook + */ + public void destroy() throws Exception + { + } + /** * Create server descriptor * @@ -710,5 +718,4 @@ public class DescriptorServiceImpl implements DescriptorService, ApplicationList return serverProperties.getProperty(key, ""); } } - } diff --git a/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java b/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java index d0acb65339..85cd40e019 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java @@ -149,12 +149,19 @@ public class ChildAssocImpl implements ChildAssoc, Serializable return false; } ChildAssoc that = (ChildAssoc) obj; - return ( - EqualsHelper.nullSafeEquals(this.getTypeQName(), that.getTypeQName()) - && EqualsHelper.nullSafeEquals(this.getQname(), that.getQname()) - && EqualsHelper.nullSafeEquals(this.getParent(), that.getParent()) - && EqualsHelper.nullSafeEquals(this.getChild(), that.getChild()) - ); + if (EqualsHelper.nullSafeEquals(id, that.getId())) + { + return true; + } + else + { + return ( + EqualsHelper.nullSafeEquals(this.getChild().getId(), that.getChild().getId()) + && EqualsHelper.nullSafeEquals(this.getQname(), that.getQname()) + && EqualsHelper.nullSafeEquals(this.getParent().getId(), that.getParent().getId()) + && EqualsHelper.nullSafeEquals(this.getTypeQName(), that.getTypeQName()) + ); + } } public int hashCode() diff --git a/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlEntryImpl.java b/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlEntryImpl.java index db1032aa6d..32da2216e1 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlEntryImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/DbAccessControlEntryImpl.java @@ -79,8 +79,15 @@ public class DbAccessControlEntryImpl extends LifecycleAdapter return false; } DbAccessControlEntry other = (DbAccessControlEntry) o; - return (EqualsHelper.nullSafeEquals(this.permission, other.getPermission()) - && EqualsHelper.nullSafeEquals(this.authority, other.getAuthority())); + if (EqualsHelper.nullSafeEquals(id, other.getId())) + { + return true; + } + else + { + return (EqualsHelper.nullSafeEquals(this.permission, other.getPermission()) + && EqualsHelper.nullSafeEquals(this.authority, other.getAuthority())); + } } @Override diff --git a/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java b/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java index 40aa060edd..1e505c347d 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java @@ -137,8 +137,14 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable return false; } Node that = (Node) obj; - return (EqualsHelper.nullSafeEquals(getStore(), that.getStore()) - && EqualsHelper.nullSafeEquals(getUuid(), that.getUuid())); + if (EqualsHelper.nullSafeEquals(id, that.getId())) + { + return true; + } + else + { + return (this.getNodeRef().equals(that.getNodeRef())); + } } public int hashCode() diff --git a/source/java/org/alfresco/repo/lock/LockBehaviourImplTest.java b/source/java/org/alfresco/repo/lock/LockBehaviourImplTest.java index 81a150cf88..d89401aff7 100644 --- a/source/java/org/alfresco/repo/lock/LockBehaviourImplTest.java +++ b/source/java/org/alfresco/repo/lock/LockBehaviourImplTest.java @@ -135,8 +135,8 @@ public class LockBehaviourImplTest extends BaseSpringTest TestWithUserUtils.authenticateUser(BAD_USER_NAME, PWD, rootNodeRef, this.authenticationService); TestWithUserUtils.authenticateUser(GOOD_USER_NAME, PWD, rootNodeRef, this.authenticationService); - permissionService.setPermission(rootNodeRef, GOOD_USER_NAME.toLowerCase(), PermissionService.ALL_PERMISSIONS, true); - permissionService.setPermission(rootNodeRef, BAD_USER_NAME.toLowerCase(), PermissionService.READ, true); + permissionService.setPermission(rootNodeRef, GOOD_USER_NAME, PermissionService.ALL_PERMISSIONS, true); + permissionService.setPermission(rootNodeRef, BAD_USER_NAME, PermissionService.READ, true); } /** diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java index 847a8c7201..e20fced88e 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java @@ -31,6 +31,7 @@ import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -236,10 +237,80 @@ public class FileFolderPerformanceTester extends TestCase } } - public void test_2_ordered_1_10() throws Exception + private void readStructure( + final NodeRef parentNodeRef, + final int threadCount, + final int repetitions, + final double[] dumpPoints) { - buildStructure(rootFolderRef, 2, false, 1, 10, null); + final List children = nodeService.getChildAssocs(parentNodeRef); + Runnable runnable = new Runnable() + { + public void run() + { + // authenticate + authenticationComponent.setSystemUserAsCurrentUser(); + + for (int i = 0; i < repetitions; i++) + { + // read the contents of each folder + for (ChildAssociationRef childAssociationRef : children) + { + final NodeRef folderRef = childAssociationRef.getChildRef(); + TransactionWork readWork = new TransactionWork() + { + public Object doWork() throws Exception + { + // read the child associations of the folder + nodeService.getChildAssocs(folderRef); + // get the type + nodeService.getType(folderRef); + // done + return null; + }; + }; + TransactionUtil.executeInUserTransaction(transactionService, readWork, true); + } + } + } + }; + + // kick off the required number of threads + logger.debug("\n" + + "Starting " + threadCount + + " threads reading properties and children of " + children.size() + + " folder " + repetitions + + " times."); + ThreadGroup threadGroup = new ThreadGroup(getName()); + Thread[] threads = new Thread[threadCount]; + for (int i = 0; i < threadCount; i++) + { + threads[i] = new Thread(threadGroup, runnable, String.format("FileReader-%02d", i)); + threads[i].start(); + } + // join each thread so that we wait for them all to finish + for (int i = 0; i < threads.length; i++) + { + try + { + threads[i].join(); + } + catch (InterruptedException e) + { + // not too serious - the worker threads are non-daemon + } + } } + + public void test_1_ordered_1_10() throws Exception + { + buildStructure(rootFolderRef, 1, false, 1, 10, null); + } +// public void test_1_ordered_1_10_read() throws Exception +// { +// buildStructure(rootFolderRef, 1, false, 50, 1, null); +// readStructure(rootFolderRef, 50, 1000, null); +// } // // public void test_4_ordered_10_100() throws Exception // { @@ -250,24 +321,34 @@ public class FileFolderPerformanceTester extends TestCase // { // buildStructure(rootFolderRef, 4, true, 10, 100, new double[] {0.25, 0.50, 0.75}); // } - public void test_4_shuffled_100_100() throws Exception +// public void test_1_ordered_100_100() throws Exception +// { +// buildStructure( +// rootFolderRef, +// 1, +// false, +// 100, +// 100, +// new double[] {0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90}); +// } + public void test_1_shuffled_10_400() throws Exception + { + buildStructure( + rootFolderRef, + 1, + true, + 10, + 400, + new double[] {0.05, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90}); + } + public void test_4_shuffled_10_100() throws Exception { buildStructure( rootFolderRef, 4, true, + 10, 100, - 100, - new double[] {0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90}); + new double[] {0.05, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90}); } -// public void test_4_shuffled_1000_1000() throws Exception -// { -// buildStructure( -// rootFolderRef, -// 4, -// true, -// 1000, -// 1000, -// new double[] {0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90}); -// } } diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java index 3fbf309946..9e9db3ec01 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java @@ -45,6 +45,9 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.search.QueryParameterDefinition; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.ResultSetRow; +import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; @@ -59,17 +62,6 @@ import org.apache.commons.logging.LogFactory; */ public class FileFolderServiceImpl implements FileFolderService { - /** Shallow search for all files */ - private static final String XPATH_QUERY_SHALLOW_FILES = - "./*" + - "[(subtypeOf('" + ContentModel.TYPE_CONTENT + "'))]"; - - /** Shallow search for all folder */ - private static final String XPATH_QUERY_SHALLOW_FOLDERS = - "./*" + - "[not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" + - " and (subtypeOf('" + ContentModel.TYPE_FOLDER + "'))]"; - /** Shallow search for files and folders with a name pattern */ private static final String XPATH_QUERY_SHALLOW_ALL = "./*" + @@ -77,6 +69,23 @@ public class FileFolderServiceImpl implements FileFolderService " and not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" + " and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "'))]"; + /** Shallow search for all files and folders */ + private static final String LUCENE_QUERY_SHALLOW_ALL = + "+PARENT:\"${cm:parent}\"" + + "-TYPE:\"" + ContentModel.TYPE_SYSTEM_FOLDER + "\" "; + + /** Shallow search for all files and folders */ + private static final String LUCENE_QUERY_SHALLOW_FOLDERS = + "+PARENT:\"${cm:parent}\"" + + "-TYPE:\"" + ContentModel.TYPE_SYSTEM_FOLDER + "\" " + + "+TYPE:\"" + ContentModel.TYPE_FOLDER + "\" "; + + /** Shallow search for all files and folders */ + private static final String LUCENE_QUERY_SHALLOW_FILES = + "+PARENT:\"${cm:parent}\"" + + "-TYPE:\"" + ContentModel.TYPE_SYSTEM_FOLDER + "\" " + + "+TYPE:\"" + ContentModel.TYPE_CONTENT + "\" "; + /** Deep search for files and folders with a name pattern */ private static final String XPATH_QUERY_DEEP_ALL = ".//*" + @@ -85,8 +94,8 @@ public class FileFolderServiceImpl implements FileFolderService " and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "'))]"; /** empty parameters */ - private static final QueryParameterDefinition[] PARAMS_EMPTY = new QueryParameterDefinition[0]; private static final QueryParameterDefinition[] PARAMS_ANY_NAME = new QueryParameterDefinition[1]; + private static final QName PARAM_QNAME_PARENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "parent"); private static Log logger = LogFactory.getLog(FileFolderServiceImpl.class); @@ -100,6 +109,7 @@ public class FileFolderServiceImpl implements FileFolderService // TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID) private List systemPaths; + private DataTypeDefinition dataTypeNodeRef; /** * Default constructor @@ -157,6 +167,7 @@ public class FileFolderServiceImpl implements FileFolderService dictionaryService.getDataType(DataTypeDefinition.TEXT), true, "%"); + dataTypeNodeRef = dictionaryService.getDataType(DataTypeDefinition.NODE_REF); } /** @@ -202,12 +213,17 @@ public class FileFolderServiceImpl implements FileFolderService private void checkExists(NodeRef parentFolderRef, String name) throws FileExistsException { - // check for existing file or folder - List existingFileInfos = this.search(parentFolderRef, name, true, true, false); - if (existingFileInfos.size() > 0) + // the name is never a wildcard, so we can perform an exact search + List nodeRefs = searchInternal(parentFolderRef, name, true, true, false); + if (nodeRefs.size() == 0) { - throw new FileExistsException(existingFileInfos.get(0)); + // no match + return; } + // we found a match, so raise the exception + NodeRef duplicateNodeRef = nodeRefs.get(0); + FileInfo duplicateFileInfo = toFileInfo(duplicateNodeRef); + throw new FileExistsException(duplicateFileInfo); } /** @@ -258,18 +274,10 @@ public class FileFolderServiceImpl implements FileFolderService } } - /** - * TODO: Use Lucene search to get file attributes without having to visit the node service - */ public List list(NodeRef contextNodeRef) { // execute the query - List nodeRefs = searchService.selectNodes( - contextNodeRef, - XPATH_QUERY_SHALLOW_ALL, - PARAMS_ANY_NAME, - namespaceService, - false); + List nodeRefs = luceneSearch(contextNodeRef, true, true); // convert the noderefs List results = toFileInfo(nodeRefs); // done @@ -282,18 +290,10 @@ public class FileFolderServiceImpl implements FileFolderService return results; } - /** - * TODO: Use Lucene search to get file attributes without having to visit the node service - */ public List listFiles(NodeRef contextNodeRef) { // execute the query - List nodeRefs = searchService.selectNodes( - contextNodeRef, - XPATH_QUERY_SHALLOW_FILES, - PARAMS_EMPTY, - namespaceService, - false); + List nodeRefs = luceneSearch(contextNodeRef, false, true); // convert the noderefs List results = toFileInfo(nodeRefs); // done @@ -306,18 +306,10 @@ public class FileFolderServiceImpl implements FileFolderService return results; } - /** - * TODO: Use Lucene search to get file attributes without having to visit the node service - */ public List listFolders(NodeRef contextNodeRef) { // execute the query - List nodeRefs = searchService.selectNodes( - contextNodeRef, - XPATH_QUERY_SHALLOW_FOLDERS, - PARAMS_EMPTY, - namespaceService, - false); + List nodeRefs = luceneSearch(contextNodeRef, true, false); // convert the noderefs List results = toFileInfo(nodeRefs); // done @@ -338,6 +330,7 @@ public class FileFolderServiceImpl implements FileFolderService return search(contextNodeRef, namePattern, true, true, includeSubFolders); } + private static final String LUCENE_MULTI_CHAR_WILDCARD = "*"; /** * Full search with all options */ @@ -348,50 +341,9 @@ public class FileFolderServiceImpl implements FileFolderService boolean folderSearch, boolean includeSubFolders) { - // shortcut if the search is requesting nothing - if (!fileSearch && !folderSearch) - { - return Collections.emptyList(); - } + // get the raw nodeRefs + List nodeRefs = searchInternal(contextNodeRef, namePattern, fileSearch, folderSearch, includeSubFolders); - // if the name pattern is null, then we use the ANY pattern - QueryParameterDefinition[] params = null; - if (namePattern != null) - { - // the interface specifies the Lucene syntax, so perform a conversion - namePattern = SearchLanguageConversion.convert( - SearchLanguageConversion.DEF_LUCENE, - SearchLanguageConversion.DEF_XPATH_LIKE, - namePattern); - - params = new QueryParameterDefinition[1]; - params[0] = new QueryParameterDefImpl( - ContentModel.PROP_NAME, - dictionaryService.getDataType(DataTypeDefinition.TEXT), - true, - namePattern); - } - else - { - params = PARAMS_ANY_NAME; - } - // determine the correct query to use - String query = null; - if (includeSubFolders) - { - query = XPATH_QUERY_DEEP_ALL; - } - else - { - query = XPATH_QUERY_SHALLOW_ALL; - } - // execute the query - List nodeRefs = searchService.selectNodes( - contextNodeRef, - query, - params, - namespaceService, - false); List results = toFileInfo(nodeRefs); // eliminate unwanted files/folders Iterator iterator = results.iterator(); @@ -420,6 +372,133 @@ public class FileFolderServiceImpl implements FileFolderService } return results; } + + /** + * Performs a full search, but doesn't translate the node references into + * file info objects. This allows {@link #checkExists(NodeRef, String)} to + * bypass the retrieval of node properties. + */ + public List searchInternal( + NodeRef contextNodeRef, + String namePattern, + boolean fileSearch, + boolean folderSearch, + boolean includeSubFolders) + { + // shortcut if the search is requesting nothing + if (!fileSearch && !folderSearch) + { + return Collections.emptyList(); + } + + if (namePattern == null) + { + namePattern = LUCENE_MULTI_CHAR_WILDCARD; // default to wildcard + } + // now check if we can use Lucene to handle this query + boolean useLucene = false; + boolean anyName = namePattern.equals(LUCENE_MULTI_CHAR_WILDCARD); + if (!includeSubFolders && anyName) + { + // Lucene only handles any name or exact name + useLucene = true; + } + + List nodeRefs = null; + if (!useLucene) // go with the XPath queries + { + // if the name pattern is null, then we use the ANY pattern + QueryParameterDefinition[] params = null; + if (namePattern != null) + { + // the interface specifies the Lucene syntax, so perform a conversion + namePattern = SearchLanguageConversion.convert( + SearchLanguageConversion.DEF_LUCENE, + SearchLanguageConversion.DEF_XPATH_LIKE, + namePattern); + + params = new QueryParameterDefinition[1]; + params[0] = new QueryParameterDefImpl( + ContentModel.PROP_NAME, + dictionaryService.getDataType(DataTypeDefinition.TEXT), + true, + namePattern); + } + else + { + params = PARAMS_ANY_NAME; + } + // determine the correct query to use + String query = null; + if (includeSubFolders) + { + query = XPATH_QUERY_DEEP_ALL; + } + else + { + query = XPATH_QUERY_SHALLOW_ALL; + } + // execute the query + nodeRefs = searchService.selectNodes( + contextNodeRef, + query, + params, + namespaceService, + false); + } + else // go with Lucene queries + { + nodeRefs = luceneSearch(contextNodeRef, folderSearch, fileSearch); + } + // done + return nodeRefs; + } + + private List luceneSearch(NodeRef contextNodeRef, boolean folders, boolean files) + { + SearchParameters params = new SearchParameters(); + params.setLanguage(SearchService.LANGUAGE_LUCENE); + params.addStore(contextNodeRef.getStoreRef()); + // set the parent parameter + QueryParameterDefinition parentParamDef = new QueryParameterDefImpl( + PARAM_QNAME_PARENT, + dataTypeNodeRef, + true, + contextNodeRef.toString()); + params.addQueryParameterDefinition(parentParamDef); + if (folders && files) // search for both files and folders + { + params.setQuery(LUCENE_QUERY_SHALLOW_ALL); + } + else if (folders) // search for folders only + { + params.setQuery(LUCENE_QUERY_SHALLOW_FOLDERS); + } + else if (files) // search for files only + { + params.setQuery(LUCENE_QUERY_SHALLOW_FILES); + } + else + { + throw new IllegalArgumentException("Must search for either files or folders or both"); + } + ResultSet rs = searchService.query(params); + int length = rs.length(); + List nodeRefs = new ArrayList(length); + try + { + for (ResultSetRow row : rs) + { + nodeRefs.add(row.getNodeRef()); + } + } + finally + { + rs.close(); + } + // done + return nodeRefs; + } /** * @see #move(NodeRef, NodeRef, String) @@ -467,10 +546,10 @@ public class FileFolderServiceImpl implements FileFolderService targetParentRef = assocRef.getParentRef(); } + // there is nothing to do if both the name and parent folder haven't changed boolean checkExists = true; if (targetParentRef.equals(assocRef.getParentRef())) { - // there is nothing to do if both the name and parent folder haven't changed if (newName.equals(beforeFileInfo.getName())) { if (logger.isDebugEnabled()) @@ -484,17 +563,15 @@ public class FileFolderServiceImpl implements FileFolderService } else if (newName.equalsIgnoreCase(beforeFileInfo.getName())) { - // name has only changed case so don't bother with exists check checkExists = false; } } - // check for existing file or folder (if name has changed) + // check for existing file or folder if (checkExists) { checkExists(targetParentRef, newName); } - QName qname = QName.createQName( NamespaceService.CONTENT_MODEL_1_0_URI, diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java index dc9bb09cbf..65853b26bb 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java @@ -417,19 +417,20 @@ public class FileFolderServiceImplTest extends TestCase { // create a completely new path below the root List namePath = new ArrayList(4); - namePath.add("A"); - namePath.add("B"); - namePath.add("C"); - namePath.add("D"); + namePath.add("AAA"); + namePath.add("BBB"); + namePath.add("CCC"); + namePath.add("DDD"); FileInfo lastFileInfo = fileFolderService.makeFolders(rootNodeRef, namePath, ContentModel.TYPE_FOLDER); assertNotNull("First makeFolder failed", lastFileInfo); // check that a repeat works + FileInfo lastFileInfoAgain = fileFolderService.makeFolders(rootNodeRef, namePath, ContentModel.TYPE_FOLDER); assertNotNull("Repeat makeFolders failed", lastFileInfoAgain); assertEquals("Repeat created new leaf", lastFileInfo.getNodeRef(), lastFileInfoAgain.getNodeRef()); // check that it worked - List checkInfos = fileFolderService.search(rootNodeRef, "D", false, true, true); + List checkInfos = fileFolderService.search(rootNodeRef, "DDD", false, true, true); assertEquals("Expected to find a result", 1, checkInfos.size()); // get the path List checkPathInfos = fileFolderService.getNamePath(rootNodeRef, checkInfos.get(0).getNodeRef()); @@ -442,6 +443,27 @@ public class FileFolderServiceImplTest extends TestCase } } + /** + * Lucene only indexes terms that are 3 characters or more + */ + public void testMakeFoldersShortNames() throws Exception + { + // create a completely new path below the root + List namePath = new ArrayList(4); + namePath.add("A"); + namePath.add("B"); + namePath.add("C"); + namePath.add("D"); + + FileInfo lastFileInfo = fileFolderService.makeFolders(rootNodeRef, namePath, ContentModel.TYPE_FOLDER); + assertNotNull("First makeFolder failed", lastFileInfo); + // check that a repeat works + + FileInfo lastFileInfoAgain = fileFolderService.makeFolders(rootNodeRef, namePath, ContentModel.TYPE_FOLDER); + assertNotNull("Repeat makeFolders failed", lastFileInfoAgain); + assertEquals("Repeat created new leaf", lastFileInfo.getNodeRef(), lastFileInfoAgain.getNodeRef()); + } + public void testGetNamePath() throws Exception { FileInfo fileInfo = getByName(NAME_L1_FILE_A, false); diff --git a/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java b/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java index c42209c9a8..687446254e 100644 --- a/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java @@ -42,6 +42,7 @@ import org.alfresco.repo.node.NodeServicePolicies.OnCreateStorePolicy; import org.alfresco.repo.node.NodeServicePolicies.OnDeleteAssociationPolicy; import org.alfresco.repo.node.NodeServicePolicies.OnDeleteChildAssociationPolicy; import org.alfresco.repo.node.NodeServicePolicies.OnDeleteNodePolicy; +import org.alfresco.repo.node.NodeServicePolicies.OnMoveNodePolicy; import org.alfresco.repo.node.NodeServicePolicies.OnRemoveAspectPolicy; import org.alfresco.repo.node.NodeServicePolicies.OnUpdateNodePolicy; import org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy; @@ -96,6 +97,7 @@ public abstract class AbstractNodeServiceImpl implements NodeService private ClassPolicyDelegate onCreateStoreDelegate; private ClassPolicyDelegate beforeCreateNodeDelegate; private ClassPolicyDelegate onCreateNodeDelegate; + private ClassPolicyDelegate onMoveNodeDelegate; private ClassPolicyDelegate beforeUpdateNodeDelegate; private ClassPolicyDelegate onUpdateNodeDelegate; private ClassPolicyDelegate onUpdatePropertiesDelegate; @@ -161,6 +163,7 @@ public abstract class AbstractNodeServiceImpl implements NodeService onCreateStoreDelegate = policyComponent.registerClassPolicy(NodeServicePolicies.OnCreateStorePolicy.class); beforeCreateNodeDelegate = policyComponent.registerClassPolicy(NodeServicePolicies.BeforeCreateNodePolicy.class); onCreateNodeDelegate = policyComponent.registerClassPolicy(NodeServicePolicies.OnCreateNodePolicy.class); + onMoveNodeDelegate = policyComponent.registerClassPolicy(NodeServicePolicies.OnMoveNodePolicy.class); beforeUpdateNodeDelegate = policyComponent.registerClassPolicy(NodeServicePolicies.BeforeUpdateNodePolicy.class); onUpdateNodeDelegate = policyComponent.registerClassPolicy(NodeServicePolicies.OnUpdateNodePolicy.class); onUpdatePropertiesDelegate = policyComponent.registerClassPolicy(NodeServicePolicies.OnUpdatePropertiesPolicy.class); @@ -227,6 +230,19 @@ public abstract class AbstractNodeServiceImpl implements NodeService policy.onCreateNode(childAssocRef); } + /** + * @see NodeServicePolicies.OnMoveNodePolicy#onMoveNode(ChildAssociationRef, ChildAssociationRef) + */ + protected void invokeOnMoveNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef) + { + NodeRef childNodeRef = newChildAssocRef.getChildRef(); + // get qnames to invoke against + Set qnames = getTypeAndAspectQNames(childNodeRef); + // execute policy for node type and aspects + NodeServicePolicies.OnMoveNodePolicy policy = onMoveNodeDelegate.get(childNodeRef, qnames); + policy.onMoveNode(oldChildAssocRef, newChildAssocRef); + } + /** * @see NodeServicePolicies.BeforeUpdateNodePolicy#beforeUpdateNode(NodeRef) */ diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java index c180f2f879..4f92dbb370 100644 --- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java +++ b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java @@ -1153,6 +1153,17 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest RegexQNamePattern.MATCH_ALL); } + public static class MovePolicyTester implements NodeServicePolicies.OnMoveNodePolicy + { + public List policyAssocRefs = new ArrayList(2); + public void onMoveNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef) + { + policyAssocRefs.add(oldChildAssocRef); + policyAssocRefs.add(newChildAssocRef); + } + }; + + public void testMoveNode() throws Exception { Map assocRefs = buildNodeGraph(); @@ -1163,12 +1174,24 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest NodeRef n5Ref = n5pn7Ref.getParentRef(); NodeRef n6Ref = n6pn8Ref.getParentRef(); NodeRef n8Ref = n6pn8Ref.getChildRef(); + + MovePolicyTester policy = new MovePolicyTester(); + // bind to listen to the deletion of a node + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onMoveNode"), + policy, + new JavaBehaviour(policy, "onMoveNode")); + // move n8 to n5 ChildAssociationRef assocRef = nodeService.moveNode( n8Ref, n5Ref, ASSOC_TYPE_QNAME_TEST_CHILDREN, QName.createQName(BaseNodeServiceTest.NAMESPACE, "n5_p_n8")); + + // check that the move policy was fired + assertEquals("Move policy not fired", 2, policy.policyAssocRefs.size()); + // check that n6 is no longer the parent List n6ChildRefs = nodeService.getChildAssocs( n6Ref, diff --git a/source/java/org/alfresco/repo/node/NodeServicePolicies.java b/source/java/org/alfresco/repo/node/NodeServicePolicies.java index ae5bc60355..288e24606b 100644 --- a/source/java/org/alfresco/repo/node/NodeServicePolicies.java +++ b/source/java/org/alfresco/repo/node/NodeServicePolicies.java @@ -82,6 +82,17 @@ public interface NodeServicePolicies public void onCreateNode(ChildAssociationRef childAssocRef); } + public interface OnMoveNodePolicy extends ClassPolicy + { + /** + * Called when a node has been moved. + * + * @param oldChildAssocRef the child association reference prior to the move + * @param newChildAssocRef the child association reference after the move + */ + public void onMoveNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef); + } + public interface BeforeUpdateNodePolicy extends ClassPolicy { /** diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index 18a54f4682..e2fba6daa8 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -434,6 +434,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl nodeDaoService.deleteChildAssoc(oldAssoc, false); // create a new assoc ChildAssoc newAssoc = nodeDaoService.newChildAssoc(newParentNode, nodeToMove, true, assocTypeQName, assocQName); + ChildAssociationRef newAssocRef = newAssoc.getChildAssocRef(); // If the node is moving stores, then drag the node hierarchy with it if (movingStore) @@ -463,6 +464,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl invokeOnUpdateNode(oldParentNode.getNodeRef()); invokeOnUpdateNode(newParentRef); } + invokeOnMoveNode(oldAssocRef, newAssocRef); // update the node status nodeDaoService.recordChangeId(nodeToMoveRef); diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneAnalyser.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneAnalyser.java index f59560a93a..d810438117 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneAnalyser.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneAnalyser.java @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.Map; import org.alfresco.repo.search.impl.lucene.analysis.PathAnalyser; +import org.alfresco.repo.search.impl.lucene.analysis.VerbatimAnalyser; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; @@ -117,8 +118,23 @@ public class LuceneAnalyser extends Analyzer { QName propertyQName = QName.createQName(fieldName.substring(1)); PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName); - DataTypeDefinition dataType = (propertyDef == null) ? dictionaryService.getDataType(DataTypeDefinition.TEXT) : propertyDef.getDataType(); - analyser = loadAnalyzer(dataType); + if (propertyDef != null) + { + if (propertyDef.isTokenisedInIndex()) + { + DataTypeDefinition dataType = propertyDef.getDataType(); + analyser = loadAnalyzer(dataType); + } + else + { + analyser = new VerbatimAnalyser(); + } + } + else + { + DataTypeDefinition dataType = dictionaryService.getDataType(DataTypeDefinition.TEXT); + analyser = loadAnalyzer(dataType); + } } else { @@ -127,28 +143,31 @@ public class LuceneAnalyser extends Analyzer analysers.put(fieldName, analyser); return analyser; } - + private Analyzer loadAnalyzer(DataTypeDefinition dataType) { String analyserClassName = dataType.getAnalyserClassName(); try { Class clazz = Class.forName(analyserClassName); - Analyzer analyser = (Analyzer)clazz.newInstance(); + Analyzer analyser = (Analyzer) clazz.newInstance(); return analyser; } catch (ClassNotFoundException e) { - throw new RuntimeException("Unable to load analyser for property of type " + dataType.getName() + " using " + analyserClassName); + throw new RuntimeException("Unable to load analyser for property of type " + dataType.getName() + " using " + + analyserClassName); } catch (InstantiationException e) { - throw new RuntimeException("Unable to load analyser for property of type " + dataType.getName() + " using " + analyserClassName); + throw new RuntimeException("Unable to load analyser for property of type " + dataType.getName() + " using " + + analyserClassName); } catch (IllegalAccessException e) { - throw new RuntimeException("Unable to load analyser for property of type " + dataType.getName() + " using " + analyserClassName); + throw new RuntimeException("Unable to load analyser for property of type " + dataType.getName() + " using " + + analyserClassName); } } - + } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneTest.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneTest.java index 3d8c37bcbf..c65a85d4a8 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneTest.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneTest.java @@ -264,6 +264,7 @@ public class LuceneTest extends TestCase testProperties.put(QName.createQName(TEST_NAMESPACE, "category-ista"), new NodeRef(storeRef, "CategoryId")); testProperties.put(QName.createQName(TEST_NAMESPACE, "noderef-ista"), n1); testProperties.put(QName.createQName(TEST_NAMESPACE, "path-ista"), nodeService.getPath(n3)); + testProperties.put(QName.createQName(TEST_NAMESPACE, "verbatim"), " "); testProperties.put(QName.createQName(TEST_NAMESPACE, "null"), null); testProperties.put(QName.createQName(TEST_NAMESPACE, "list"), new ArrayList()); ArrayList testList = new ArrayList(); @@ -1855,6 +1856,19 @@ public class LuceneTest extends TestCase assertNotNull(results.getRow(0).getValue(QName.createQName(TEST_NAMESPACE, "path-ista"))); results.close(); + results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "\\@" + + escapeQName(QName.createQName(TEST_NAMESPACE, "verbatim")) + ":\" \"", + null, null); + assertEquals(1, results.length()); + assertNotNull(results.getRow(0).getValue(QName.createQName(TEST_NAMESPACE, "verbatim"))); + results.close(); + + results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "\\@" + + escapeQName(QName.createQName(TEST_NAMESPACE, "verbatim")) + ":\" \"", + null, null); + assertEquals(0, results.length()); + results.close(); + results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "TYPE:\"" + testType.toString() + "\"", null, null); assertEquals(1, results.length()); diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneTest_model.xml b/source/java/org/alfresco/repo/search/impl/lucene/LuceneTest_model.xml index 666297f64e..75856aaee8 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneTest_model.xml +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneTest_model.xml @@ -14,217 +14,176 @@ - - - Test Super Type - sys:container - - - d:date - true - false - - true - true - true - - - - d:double - true - false - - true - true - true - - - - d:float - true - false - - true - true - true - - - - d:long - true - false - - true - true - true - - - - d:int - true - false - - true - true - true - - - - - - - false - true - - - sys:base - false - true - - - - - - - Test Type - test:testSuperType - - - d:text - true - false - - true - true - true - - - - d:text - true - false - - true - false - true - - - - d:text - true - false - - false - true - true - - - - d:int - true - false - - true - true - true - - - - d:long - true - false - - true - true - true - - - - d:float - true - false - - true - true - true - - - - d:double - true - false - - true - true - true - - - - d:date - true - false - - true - true - true - - - - d:datetime - true - false - - true - true - true - - - - d:boolean - true - false - - true - true - true - - - - d:qname - true - false - - true - true - true - - - - d:category - true - false - - true - true - true - - - - d:noderef - true - false - - true - true - true - - - - - test:testAspect - - - + + + Test Super Type + sys:container + + + + false + true + + + sys:base + false + true + + + + + + + Test Type + test:testSuperType + + + d:text + true + false + + true + true + true + + + + d:text + true + false + + true + false + true + + + + d:text + true + false + + false + true + true + + + + d:int + true + false + + true + true + true + + + + d:long + true + false + + true + true + true + + + + d:float + true + false + + true + true + true + + + + d:double + true + false + + true + true + true + + + + d:date + true + false + + true + true + true + + + + d:datetime + true + false + + true + true + true + + + + d:boolean + true + false + + true + true + true + + + + d:qname + true + false + + true + true + true + + + + d:category + true + false + + true + true + true + + + + d:noderef + true + false + + true + true + true + + + + d:text + true + false + + true + true + false + + + + + test:testAspect + + + +>>>>>>> .merge-right.r3203 diff --git a/source/java/org/alfresco/repo/search/impl/lucene/analysis/VerbatimAnalyser.java b/source/java/org/alfresco/repo/search/impl/lucene/analysis/VerbatimAnalyser.java new file mode 100644 index 0000000000..817113327f --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/lucene/analysis/VerbatimAnalyser.java @@ -0,0 +1,22 @@ +package org.alfresco.repo.search.impl.lucene.analysis; + +import java.io.Reader; + +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.TokenStream; + +public class VerbatimAnalyser + +extends Analyzer +{ + + public VerbatimAnalyser() + { + super(); + } + + public TokenStream tokenStream(String fieldName, Reader reader) + { + return new VerbatimTokenFilter(reader); + } +} diff --git a/source/java/org/alfresco/repo/search/impl/lucene/analysis/VerbatimTokenFilter.java b/source/java/org/alfresco/repo/search/impl/lucene/analysis/VerbatimTokenFilter.java new file mode 100644 index 0000000000..b77cee9498 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/lucene/analysis/VerbatimTokenFilter.java @@ -0,0 +1,42 @@ +package org.alfresco.repo.search.impl.lucene.analysis; + +import java.io.IOException; +import java.io.Reader; + +import org.apache.lucene.analysis.Token; +import org.apache.lucene.analysis.Tokenizer; + +public class VerbatimTokenFilter extends Tokenizer +{ + boolean readInput = true; + + VerbatimTokenFilter(Reader in) + { + super(in); + } + + @Override + public Token next() throws IOException + { + if (readInput) + { + readInput = false; + StringBuilder buffer = new StringBuilder(); + int current; + char c; + while ((current = input.read()) != -1) + { + c = (char) current; + buffer.append(c); + } + + String token = buffer.toString(); + return new Token(token, 0, token.length() - 1, "VERBATIM"); + } + else + { + return null; + } + } + +} diff --git a/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java b/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java index d7ae296206..e307338a4a 100644 --- a/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java +++ b/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java @@ -39,7 +39,6 @@ import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; import net.sf.acegisecurity.providers.dao.SaltSource; import org.alfresco.model.ContentModel; -import org.alfresco.repo.security.permissions.PermissionServiceSPI; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.NodeRef; @@ -89,8 +88,6 @@ public class AuthenticationTest extends TestCase private AuthenticationComponent authenticationComponent; - private PermissionServiceSPI permissionServiceSPI; - private UserTransaction userTransaction; private AuthenticationComponent authenticationComponentImpl; @@ -117,7 +114,8 @@ public class AuthenticationTest extends TestCase pubAuthenticationService = (AuthenticationService) ctx.getBean("AuthenticationService"); authenticationComponent = (AuthenticationComponent) ctx.getBean("authenticationComponent"); authenticationComponentImpl = (AuthenticationComponent) ctx.getBean("authenticationComponentImpl"); - permissionServiceSPI = (PermissionServiceSPI) ctx.getBean("permissionService"); + // permissionServiceSPI = (PermissionServiceSPI) + // ctx.getBean("permissionService"); dao = (MutableAuthenticationDao) ctx.getBean("alfDaoImpl"); authenticationManager = (AuthenticationManager) ctx.getBean("authenticationManager"); @@ -181,7 +179,6 @@ public class AuthenticationTest extends TestCase public void xtestScalability() { long create = 0; - long count = 0; long start; long end; @@ -207,6 +204,49 @@ public class AuthenticationTest extends TestCase authenticationComponent.clearCurrentSecurityContext(); } + public void c() + { + try + { + authenticationService.authenticate("", "".toCharArray()); + } + catch (AuthenticationException e) + { + // Expected + } + } + + public void testCreateUsers() + { + authenticationService.createAuthentication("GUEST", "".toCharArray()); + authenticationService.authenticate("GUEST", "".toCharArray()); + // Guest is reported as lower case and the authentication basically + // ignored at the moment + assertEquals("guest", authenticationService.getCurrentUserName()); + + authenticationService.createAuthentication("Andy", "".toCharArray()); + authenticationService.authenticate("Andy", "".toCharArray()); + assertEquals("Andy", authenticationService.getCurrentUserName()); + + authenticationService.createAuthentication("Mr.Woof.Banana@chocolate.chip.cookie.com", "".toCharArray()); + authenticationService.authenticate("Mr.Woof.Banana@chocolate.chip.cookie.com", "".toCharArray()); + assertEquals("Mr.Woof.Banana@chocolate.chip.cookie.com", authenticationService.getCurrentUserName()); + + authenticationService.createAuthentication("Andy_Woof/Domain", "".toCharArray()); + authenticationService.authenticate("Andy_Woof/Domain", "".toCharArray()); + assertEquals("Andy_Woof/Domain", authenticationService.getCurrentUserName()); + + authenticationService.createAuthentication("Andy_ Woof/Domain", "".toCharArray()); + authenticationService.authenticate("Andy_ Woof/Domain", "".toCharArray()); + assertEquals("Andy_ Woof/Domain", authenticationService.getCurrentUserName()); + + + authenticationService.createAuthentication("Andy `\u00ac\u00a6!\u00a3$%^&*()-_=+\t\n\u0000[]{};'#:@~,./<>?\\|", "".toCharArray()); + authenticationService.authenticate("Andy `\u00ac\u00a6!\u00a3$%^&*()-_=+\t\n\u0000[]{};'#:@~,./<>?\\|", "".toCharArray()); + assertEquals("Andy `\u00ac\u00a6!\u00a3$%^&*()-_=+\t\n\u0000[]{};'#:@~,./<>?\\|", authenticationService.getCurrentUserName()); + + } + public void testCreateAndyUserAndOtherCRUD() throws NoSuchAlgorithmException, UnsupportedEncodingException { RepositoryAuthenticationDao dao = new RepositoryAuthenticationDao(); @@ -225,7 +265,7 @@ public class AuthenticationTest extends TestCase UserDetails AndyDetails = (UserDetails) dao.loadUserByUsername("Andy"); assertNotNull(AndyDetails); - assertEquals(dao.getUserNamesAreCaseSensitive() ? "Andy" : "andy", AndyDetails.getUsername()); + assertEquals("Andy", AndyDetails.getUsername()); // assertNotNull(dao.getSalt(AndyDetails)); assertTrue(AndyDetails.isAccountNonExpired()); assertTrue(AndyDetails.isAccountNonLocked()); @@ -240,7 +280,7 @@ public class AuthenticationTest extends TestCase dao.updateUser("Andy", "carrot".toCharArray()); UserDetails newDetails = (UserDetails) dao.loadUserByUsername("Andy"); assertNotNull(newDetails); - assertEquals(dao.getUserNamesAreCaseSensitive() ? "Andy" : "andy", newDetails.getUsername()); + assertEquals("Andy", newDetails.getUsername()); // assertNotNull(dao.getSalt(newDetails)); assertTrue(newDetails.isAccountNonExpired()); assertTrue(newDetails.isAccountNonLocked()); @@ -624,7 +664,7 @@ public class AuthenticationTest extends TestCase authenticationService.authenticate("Andy", "auth1".toCharArray()); // assert the user is authenticated - assertEquals(dao.getUserNamesAreCaseSensitive() ? "Andy" : "andy", authenticationService.getCurrentUserName()); + assertEquals("Andy", authenticationService.getCurrentUserName()); // delete the user authentication object authenticationService.clearCurrentSecurityContext(); @@ -660,7 +700,7 @@ public class AuthenticationTest extends TestCase authenticationService.authenticate("Andy", "auth1".toCharArray()); // assert the user is authenticated - assertEquals(dao.getUserNamesAreCaseSensitive() ? "Andy" : "andy", authenticationService.getCurrentUserName()); + assertEquals("Andy", authenticationService.getCurrentUserName()); // delete the user authentication object authenticationService.clearCurrentSecurityContext(); @@ -696,7 +736,7 @@ public class AuthenticationTest extends TestCase authenticationService.authenticate("Andy", "auth1".toCharArray()); // assert the user is authenticated - assertEquals(dao.getUserNamesAreCaseSensitive() ? "Andy" : "andy", authenticationService.getCurrentUserName()); + assertEquals("Andy", authenticationService.getCurrentUserName()); // delete the user authentication object authenticationService.clearCurrentSecurityContext(); @@ -742,7 +782,7 @@ public class AuthenticationTest extends TestCase authenticationService.authenticate("Andy", "auth1".toCharArray()); // assert the user is authenticated - assertEquals(dao.getUserNamesAreCaseSensitive() ? "Andy" : "andy", authenticationService.getCurrentUserName()); + assertEquals("Andy", authenticationService.getCurrentUserName()); // delete the user authentication object authenticationService.clearCurrentSecurityContext(); @@ -798,7 +838,7 @@ public class AuthenticationTest extends TestCase authenticationService.authenticate("Andy", "auth1".toCharArray()); // assert the user is authenticated - assertEquals(dao.getUserNamesAreCaseSensitive() ? "Andy" : "andy", authenticationService.getCurrentUserName()); + assertEquals("Andy", authenticationService.getCurrentUserName()); // delete the user authentication object authenticationService.clearCurrentSecurityContext(); @@ -857,7 +897,7 @@ public class AuthenticationTest extends TestCase authenticationService.authenticate("Andy", "auth1".toCharArray()); // assert the user is authenticated - assertEquals(dao.getUserNamesAreCaseSensitive() ? "Andy" : "andy", authenticationService.getCurrentUserName()); + assertEquals("Andy", authenticationService.getCurrentUserName()); // delete the user authentication object authenticationService.clearCurrentSecurityContext(); @@ -918,7 +958,7 @@ public class AuthenticationTest extends TestCase pubAuthenticationService.authenticate("Andy", "auth1".toCharArray()); // assert the user is authenticated - assertEquals(dao.getUserNamesAreCaseSensitive() ? "Andy" : "andy", authenticationService.getCurrentUserName()); + assertEquals("Andy", authenticationService.getCurrentUserName()); // delete the user authentication object pubAuthenticationService.clearCurrentSecurityContext(); @@ -966,7 +1006,7 @@ public class AuthenticationTest extends TestCase pubAuthenticationService.authenticate("Andy", "auth1".toCharArray()); // assert the user is authenticated - assertEquals(dao.getUserNamesAreCaseSensitive() ? "Andy" : "andy", authenticationService.getCurrentUserName()); + assertEquals("Andy", authenticationService.getCurrentUserName()); // delete the user authentication object pubAuthenticationService.clearCurrentSecurityContext(); @@ -1013,7 +1053,7 @@ public class AuthenticationTest extends TestCase pubAuthenticationService.authenticate("Andy", "auth1".toCharArray()); // assert the user is authenticated - assertEquals(dao.getUserNamesAreCaseSensitive() ? "Andy" : "andy", authenticationService.getCurrentUserName()); + assertEquals("Andy", authenticationService.getCurrentUserName()); // delete the user authentication object pubAuthenticationService.clearCurrentSecurityContext(); @@ -1085,7 +1125,7 @@ public class AuthenticationTest extends TestCase pubAuthenticationService.authenticate("Andy", "auth1".toCharArray()); // assert the user is authenticated - assertEquals(dao.getUserNamesAreCaseSensitive() ? "Andy" : "andy", authenticationService.getCurrentUserName()); + assertEquals("Andy", authenticationService.getCurrentUserName()); // delete the user authentication object pubAuthenticationService.clearCurrentSecurityContext(); @@ -1114,7 +1154,7 @@ public class AuthenticationTest extends TestCase pubAuthenticationService.invalidateTicket(ticket); } - + public void testPubAuthenticationService0() { // pubAuthenticationService.authenticateAsGuest(); @@ -1145,7 +1185,7 @@ public class AuthenticationTest extends TestCase pubAuthenticationService.authenticate("Andy", "auth1".toCharArray()); // assert the user is authenticated - assertEquals(dao.getUserNamesAreCaseSensitive() ? "Andy" : "andy", authenticationService.getCurrentUserName()); + assertEquals("Andy", authenticationService.getCurrentUserName()); // delete the user authentication object pubAuthenticationService.clearCurrentSecurityContext(); @@ -1172,21 +1212,21 @@ public class AuthenticationTest extends TestCase // destroy the ticket instance pubAuthenticationService.invalidateTicket(ticket); - + authenticationComponent.clearCurrentSecurityContext(); - + pubAuthenticationService.authenticate("Andy", "auth3".toCharArray()); pubAuthenticationService.updateAuthentication("Andy", "auth3".toCharArray(), "auth4".toCharArray()); pubAuthenticationService.authenticate("Andy", "auth4".toCharArray()); - + try { - pubAuthenticationService.updateAuthentication("Andy", "auth3".toCharArray(), "auth4".toCharArray()); - fail("Should not be able to update"); + pubAuthenticationService.updateAuthentication("Andy", "auth3".toCharArray(), "auth4".toCharArray()); + fail("Should not be able to update"); } - catch(AuthenticationException ae) + catch (AuthenticationException ae) { - + } } @@ -1202,7 +1242,7 @@ public class AuthenticationTest extends TestCase authenticationService.createAuthentication("Andy", "auth1".toCharArray()); authenticationComponent.setCurrentUser("Andy"); - assertEquals(dao.getUserNamesAreCaseSensitive() ? "Andy" : "andy", authenticationService.getCurrentUserName()); + assertEquals("Andy", authenticationService.getCurrentUserName()); // authenticationService.deleteAuthentication("andy"); } diff --git a/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java index 55fbf2d4cc..553c548ec2 100644 --- a/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/DefaultMutableAuthenticationDao.java @@ -22,7 +22,6 @@ import net.sf.acegisecurity.UserDetails; import net.sf.acegisecurity.providers.dao.UsernameNotFoundException; import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.service.cmr.repository.StoreRef; import org.springframework.dao.DataAccessException; /** @@ -246,16 +245,6 @@ public class DefaultMutableAuthenticationDao implements MutableAuthenticationDao { throw new AlfrescoRuntimeException("Not implemented"); } - - /** - * Are user names case sensitive? - * - * @return - */ - public boolean getUserNamesAreCaseSensitive() - { - throw new AlfrescoRuntimeException("Not implemented"); - } /** * Return the user details for the specified user diff --git a/source/java/org/alfresco/repo/security/authentication/MutableAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/MutableAuthenticationDao.java index 22389bbc4a..ca94278a41 100644 --- a/source/java/org/alfresco/repo/security/authentication/MutableAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/MutableAuthenticationDao.java @@ -184,11 +184,4 @@ public interface MutableAuthenticationDao extends AuthenticationDao, SaltSource */ public String getMD4HashedPassword(String userName); - /** - * Are user names case sensitive? - * - * @return - */ - public boolean getUserNamesAreCaseSensitive(); - } diff --git a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java index d9a544d25d..1f9dc60d7a 100644 --- a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java @@ -31,6 +31,7 @@ import net.sf.acegisecurity.providers.encoding.PasswordEncoder; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; +import org.alfresco.repo.search.impl.lucene.LuceneQueryParser; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; @@ -54,6 +55,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao private NamespacePrefixResolver namespacePrefixResolver; + @SuppressWarnings("unused") private DictionaryService dictionaryService; private SearchService searchService; @@ -97,20 +99,23 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao this.searchService = searchService; } - public UserDetails loadUserByUsername(String caseSensitiveUserName) throws UsernameNotFoundException, + public UserDetails loadUserByUsername(String incomingUserName) throws UsernameNotFoundException, DataAccessException { - String userName = userNamesAreCaseSensitive ? caseSensitiveUserName : caseSensitiveUserName.toLowerCase(); - NodeRef userRef = getUserOrNull(userName); + NodeRef userRef = getUserOrNull(incomingUserName); if (userRef == null) { - throw new UsernameNotFoundException("Could not find user by userName: " + caseSensitiveUserName); + throw new UsernameNotFoundException("Could not find user by userName: " + incomingUserName); } Map properties = nodeService.getProperties(userRef); String password = DefaultTypeConverter.INSTANCE.convert(String.class, properties .get(ContentModel.PROP_PASSWORD)); + // Report back the user name as stored on the user + String userName = DefaultTypeConverter.INSTANCE.convert(String.class, properties + .get(ContentModel.PROP_USER_USERNAME)); + GrantedAuthority[] gas = new GrantedAuthority[1]; gas[0] = new GrantedAuthorityImpl("ROLE_AUTHENTICATED"); @@ -119,12 +124,20 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao return ud; } - public NodeRef getUserOrNull(String caseSensitiveUserName) + public NodeRef getUserOrNull(String searchUserName) { - String userName = userNamesAreCaseSensitive ? caseSensitiveUserName : caseSensitiveUserName.toLowerCase(); + if(searchUserName == null) + { + return null; + } + if(searchUserName.length() == 0) + { + return null; + } + SearchParameters sp = new SearchParameters(); sp.setLanguage(SearchService.LANGUAGE_LUCENE); - sp.setQuery("@usr\\:username:" + userName); + sp.setQuery("@usr\\:username:\"" + searchUserName + "\""); sp.addStore(STOREREF_USERS); sp.excludeDataInTheCurrentTransaction(false); @@ -134,6 +147,8 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao { rs = searchService.query(sp); + NodeRef returnRef = null; + for (ResultSetRow row : rs) { @@ -142,12 +157,39 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao { String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty( nodeRef, ContentModel.PROP_USER_USERNAME)); - if (realUserName.equals(userName)) + + if (userNamesAreCaseSensitive) { - return nodeRef; + if (realUserName.equals(searchUserName)) + { + if(returnRef == null) + { + returnRef = nodeRef; + } + else + { + throw new AlfrescoRuntimeException("Found more than one user for "+searchUserName+ " (case sensitive)"); + } + } + } + else + { + if (realUserName.equalsIgnoreCase(searchUserName)) + { + if(returnRef == null) + { + returnRef = nodeRef; + } + else + { + throw new AlfrescoRuntimeException("Found more than one user for "+searchUserName+ " (case insensitive)"); + } + } } } } + + return returnRef; } finally { @@ -156,21 +198,18 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao rs.close(); } } - - return null; } public void createUser(String caseSensitiveUserName, char[] rawPassword) throws AuthenticationException { - String userName = userNamesAreCaseSensitive ? caseSensitiveUserName : caseSensitiveUserName.toLowerCase(); - NodeRef userRef = getUserOrNull(userName); + NodeRef userRef = getUserOrNull(caseSensitiveUserName); if (userRef != null) { - throw new AuthenticationException("User already exists: " + userName); + throw new AuthenticationException("User already exists: " + caseSensitiveUserName); } NodeRef typesNode = getUserFolderLocation(); Map properties = new HashMap(); - properties.put(ContentModel.PROP_USER_USERNAME, userName); + properties.put(ContentModel.PROP_USER_USERNAME, caseSensitiveUserName); String salt = null; // GUID.generate(); properties.put(ContentModel.PROP_SALT, salt); properties.put(ContentModel.PROP_PASSWORD, passwordEncoder.encodePassword(new String(rawPassword), salt)); @@ -178,11 +217,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao properties.put(ContentModel.PROP_CREDENTIALS_EXPIRE, Boolean.valueOf(false)); properties.put(ContentModel.PROP_ENABLED, Boolean.valueOf(true)); properties.put(ContentModel.PROP_ACCOUNT_LOCKED, Boolean.valueOf(false)); - nodeService.createNode( - typesNode, - ContentModel.ASSOC_CHILDREN, - ContentModel.TYPE_USER, - ContentModel.TYPE_USER, + nodeService.createNode(typesNode, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_USER, ContentModel.TYPE_USER, properties); } @@ -190,11 +225,10 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao private NodeRef getUserFolderLocation() { QName qnameAssocSystem = QName.createQName("sys", "system", namespacePrefixResolver); - QName qnameAssocUsers = QName.createQName("sys", "people", namespacePrefixResolver); // see AR-527 + QName qnameAssocUsers = QName.createQName("sys", "people", namespacePrefixResolver); // see + // AR-527 NodeRef rootNode = nodeService.getRootNode(STOREREF_USERS); - List results = nodeService.getChildAssocs( - rootNode, - RegexQNamePattern.MATCH_ALL, + List results = nodeService.getChildAssocs(rootNode, RegexQNamePattern.MATCH_ALL, qnameAssocSystem); NodeRef sysNodeRef = null; if (results.size() == 0) @@ -205,10 +239,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao { sysNodeRef = results.get(0).getChildRef(); } - results = nodeService.getChildAssocs( - sysNodeRef, - RegexQNamePattern.MATCH_ALL, - qnameAssocUsers); + results = nodeService.getChildAssocs(sysNodeRef, RegexQNamePattern.MATCH_ALL, qnameAssocUsers); NodeRef userNodeRef = null; if (results.size() == 0) { diff --git a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPGroupExportSource.java b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPGroupExportSource.java index 1177bfef29..07a6ba87fd 100644 --- a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPGroupExportSource.java +++ b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPGroupExportSource.java @@ -19,7 +19,6 @@ package org.alfresco.repo.security.authentication.ldap; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; -import java.io.IOException; import java.io.Writer; import java.util.Collection; import java.util.HashMap; diff --git a/source/java/org/alfresco/repo/security/authentication/ntlm/NTLMAuthenticationComponentImpl.java b/source/java/org/alfresco/repo/security/authentication/ntlm/NTLMAuthenticationComponentImpl.java index a5eb84628b..7bdbbf5b5d 100644 --- a/source/java/org/alfresco/repo/security/authentication/ntlm/NTLMAuthenticationComponentImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/ntlm/NTLMAuthenticationComponentImpl.java @@ -662,10 +662,9 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo } else { - // Set using the user name, lowercase the name if hte person service is case insensitive + // Set using the user name - if ( m_personService.getUserNamesAreCaseSensitive() == false) - username = username.toLowerCase(); + setCurrentUser( username); // DEBUG @@ -838,10 +837,8 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo } else { - // Set using the user name, lowercase the name if the person service is case insensitive + // Set using the user name - if ( m_personService.getUserNamesAreCaseSensitive() == false) - username = username.toLowerCase(); setCurrentUser( username); // DEBUG diff --git a/source/java/org/alfresco/repo/security/authentication/ntlm/NullMutableAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/ntlm/NullMutableAuthenticationDao.java index 4472228415..4b6f3bbb01 100644 --- a/source/java/org/alfresco/repo/security/authentication/ntlm/NullMutableAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/ntlm/NullMutableAuthenticationDao.java @@ -294,18 +294,6 @@ public class NullMutableAuthenticationDao implements MutableAuthenticationDao // return null; } - - /** - * Are user names case sensitive? - * - * @return - */ - public boolean getUserNamesAreCaseSensitive() - { - throw new AlfrescoRuntimeException("Not implemented"); - -// return false; - } /** * Return the user details for the specified user diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java b/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java index 0fa7e6ed31..19262467a3 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java +++ b/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java @@ -118,7 +118,7 @@ public class AuthorityDAOImpl implements AuthorityDAO nodeService.setProperty(parentRef, ContentModel.PROP_MEMBERS, members); userToAuthorityCache.remove(childName); } - else + else if (AuthorityType.getAuthorityType(childName).equals(AuthorityType.GROUP)) { NodeRef childRef = getAuthorityOrNull(childName); if (childRef == null) @@ -128,6 +128,10 @@ public class AuthorityDAOImpl implements AuthorityDAO nodeService.addChild(parentRef, childRef, ContentModel.ASSOC_MEMBER, QName.createQName("usr", childName, namespacePrefixResolver)); } + else + { + throw new AlfrescoRuntimeException("Authorities of the type "+AuthorityType.getAuthorityType(childName)+" may not be added to other authorities"); + } } diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java b/source/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java index ca8a0d1cd1..f1103dc8e7 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java +++ b/source/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java @@ -156,6 +156,13 @@ public class AuthorityServiceImpl implements AuthorityService public void addAuthority(String parentName, String childName) { + if (AuthorityType.getAuthorityType(childName).equals(AuthorityType.USER)) + { + if(!personService.personExists(childName)) + { + throw new AuthorityException("The person "+childName+" does not exist and can not be added to a group"); + } + } authorityDAO.addAuthority(parentName, childName); } diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java b/source/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java index 4bf6925881..5c28b1e4de 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java +++ b/source/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java @@ -310,6 +310,8 @@ public class AuthorityServiceTest extends TestCase public void testCreateAuthTree() { + personService.getPerson("andy"); + String auth1; String auth2; String auth3; @@ -339,12 +341,12 @@ public class AuthorityServiceTest extends TestCase assertEquals(5, pubAuthorityService.getAllAuthorities(AuthorityType.GROUP).size()); assertEquals(2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size()); - assertEquals(2, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); + assertEquals(3, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); pubAuthorityService.addAuthority(auth5, "andy"); assertEquals(5, pubAuthorityService.getAllAuthorities(AuthorityType.GROUP).size()); assertEquals(2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size()); // The next call looks for people not users :-) - assertEquals(2, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); + assertEquals(3, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); assertEquals(2, pubAuthorityService.getContainingAuthorities(null, "andy", false).size()); assertTrue(pubAuthorityService.getContainingAuthorities(null, "andy", false).contains(auth5)); assertTrue(pubAuthorityService.getContainingAuthorities(null, "andy", false).contains(auth2)); @@ -362,7 +364,7 @@ public class AuthorityServiceTest extends TestCase assertEquals(5, pubAuthorityService.getAllAuthorities(AuthorityType.GROUP).size()); assertEquals(2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size()); // The next call looks for people not users :-) - assertEquals(2, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); + assertEquals(3, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); assertEquals(0, pubAuthorityService.getContainingAuthorities(null, "andy", false).size()); assertEquals(1, pubAuthorityService.getContainingAuthorities(null, auth5, false).size()); assertTrue(pubAuthorityService.getContainingAuthorities(null, auth5, false).contains(auth2)); @@ -375,6 +377,8 @@ public class AuthorityServiceTest extends TestCase public void testCreateAuthNet() { + personService.getPerson("andy"); + String auth1; String auth2; String auth3; @@ -399,14 +403,14 @@ public class AuthorityServiceTest extends TestCase assertEquals(5, pubAuthorityService.getAllAuthorities(AuthorityType.GROUP).size()); assertEquals(2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size()); - assertEquals(2, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); + assertEquals(3, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); pubAuthorityService.addAuthority(auth5, "andy"); pubAuthorityService.addAuthority(auth1, "andy"); assertEquals(5, pubAuthorityService.getAllAuthorities(AuthorityType.GROUP).size()); assertEquals(2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size()); // The next call looks for people not users :-) - assertEquals(2, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); + assertEquals(3, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); assertEquals(3, pubAuthorityService.getContainingAuthorities(null, "andy", false).size()); assertTrue(pubAuthorityService.getContainingAuthorities(null, "andy", false).contains(auth5)); assertTrue(pubAuthorityService.getContainingAuthorities(null, "andy", false).contains(auth2)); @@ -425,7 +429,7 @@ public class AuthorityServiceTest extends TestCase assertEquals(5, pubAuthorityService.getAllAuthorities(AuthorityType.GROUP).size()); assertEquals(2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size()); // The next call looks for people not users :-) - assertEquals(2, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); + assertEquals(3, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); assertEquals(2, pubAuthorityService.getContainingAuthorities(null, "andy", false).size()); assertTrue(pubAuthorityService.getContainingAuthorities(null, "andy", false).contains(auth5)); assertTrue(pubAuthorityService.getContainingAuthorities(null, "andy", false).contains(auth2)); @@ -440,6 +444,8 @@ public class AuthorityServiceTest extends TestCase public void testCreateAuthNet2() { + personService.getPerson("andy"); + String auth1; String auth2; String auth3; @@ -464,14 +470,14 @@ public class AuthorityServiceTest extends TestCase assertEquals(5, pubAuthorityService.getAllAuthorities(AuthorityType.GROUP).size()); assertEquals(2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size()); - assertEquals(2, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); + assertEquals(3, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); pubAuthorityService.addAuthority(auth5, "andy"); pubAuthorityService.addAuthority(auth1, "andy"); assertEquals(5, pubAuthorityService.getAllAuthorities(AuthorityType.GROUP).size()); assertEquals(2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size()); // The next call looks for people not users :-) - assertEquals(2, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); + assertEquals(3, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); assertEquals(3, pubAuthorityService.getContainingAuthorities(null, "andy", false).size()); assertTrue(pubAuthorityService.getContainingAuthorities(null, "andy", false).contains(auth5)); assertTrue(pubAuthorityService.getContainingAuthorities(null, "andy", false).contains(auth2)); @@ -491,7 +497,7 @@ public class AuthorityServiceTest extends TestCase assertEquals(5, pubAuthorityService.getAllAuthorities(AuthorityType.GROUP).size()); assertEquals(2, pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size()); // The next call looks for people not users :-) - assertEquals(2, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); + assertEquals(3, pubAuthorityService.getAllAuthorities(AuthorityType.USER).size()); assertEquals(4, pubAuthorityService.getContainingAuthorities(null, "andy", false).size()); assertTrue(pubAuthorityService.getContainingAuthorities(null, "andy", false).contains(auth5)); assertTrue(pubAuthorityService.getContainingAuthorities(null, "andy", false).contains(auth2)); diff --git a/source/java/org/alfresco/repo/security/authority/ExtendedPermissionServiceTest.java b/source/java/org/alfresco/repo/security/authority/ExtendedPermissionServiceTest.java index cdf1fde628..ba0d590e50 100644 --- a/source/java/org/alfresco/repo/security/authority/ExtendedPermissionServiceTest.java +++ b/source/java/org/alfresco/repo/security/authority/ExtendedPermissionServiceTest.java @@ -26,6 +26,8 @@ public class ExtendedPermissionServiceTest extends AbstractPermissionTest { public void testGroupPermission() { + personService.getPerson("andy"); + runAs("andy"); assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED); permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, getPermission(PermissionService.READ), @@ -41,6 +43,8 @@ public class ExtendedPermissionServiceTest extends AbstractPermissionTest public void testDeletePermissionByRecipient() { + personService.getPerson("andy"); + runAs("andy"); assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED); permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, getPermission(PermissionService.READ), diff --git a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java index e1dd4be3f0..4a620b9173 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java @@ -1505,6 +1505,59 @@ public class PermissionServiceTest extends AbstractPermissionTest assertFalse(permissionService.hasPermission(n2, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED); } + public void testPermissionCase() + { + + runAs("andy"); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED); + runAs("lemur"); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED); + + permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, + getPermission(PermissionService.READ_CHILDREN), "Andy", AccessStatus.ALLOWED)); + permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, + getPermission(PermissionService.READ_PROPERTIES), "ANDY", AccessStatus.ALLOWED)); + permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, + getPermission(PermissionService.READ_CONTENT), "AnDy", AccessStatus.ALLOWED)); + + runAs("andy"); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED); + runAs("lemur"); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED); + assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED); + +// permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, +// getPermission(PermissionService.READ_CHILDREN), "andy", AccessStatus.ALLOWED)); +// permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, +// getPermission(PermissionService.READ_PROPERTIES), "andy", AccessStatus.ALLOWED)); +// permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, +// getPermission(PermissionService.READ_CONTENT), "andy", AccessStatus.ALLOWED)); +// +// +// runAs("andy"); +// assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED); +// assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED); +// assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED); +// assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED); +// runAs("lemur"); +// assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED); +// assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED); +// assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED); +// assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED); + + } + public void testEffectiveComposite() { diff --git a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java b/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java index a204abd7f4..e374595c68 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModel.java @@ -116,11 +116,11 @@ public class PermissionModel implements ModelDAO, InitializingBean private HashMap permissionReferenceMap; - private Map> cachedTypePermissionsExposed = - new HashMap>(128, 1.0f); + private Map> cachedTypePermissionsExposed = + new HashMap>(128, 1.0f); - private Map> cachedTypePermissionsUnexposed = - new HashMap>(128, 1.0f); + private Map> cachedTypePermissionsUnexposed = + new HashMap>(128, 1.0f); public PermissionModel() { @@ -284,9 +284,10 @@ public class PermissionModel implements ModelDAO, InitializingBean return getAllPermissionsImpl(type, true); } + @SuppressWarnings("unchecked") private Set getAllPermissionsImpl(QName type, boolean exposedOnly) { - Map> cache; + Map> cache; if (exposedOnly) { cache = this.cachedTypePermissionsExposed; @@ -295,7 +296,7 @@ public class PermissionModel implements ModelDAO, InitializingBean { cache = this.cachedTypePermissionsUnexposed; } - Set permissions = cache.get(type); + LinkedHashSet permissions = cache.get(type); if (permissions == null) { permissions = new LinkedHashSet(); @@ -310,7 +311,7 @@ public class PermissionModel implements ModelDAO, InitializingBean } cache.put(type, permissions); } - return (Set)((LinkedHashSet)permissions).clone(); + return (Set)permissions.clone(); } /** diff --git a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java index 7acae79d16..690dfa13c9 100644 --- a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java +++ b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java @@ -55,23 +55,23 @@ public class PersonServiceImpl implements PersonService private NodeService nodeService; private SearchService searchService; - + private AuthorityService authorityService; private PermissionServiceSPI permissionServiceSPI; - + private NamespacePrefixResolver namespacePrefixResolver; private boolean createMissingPeople; - private boolean userNamesAreCaseSensitive; - private String companyHomePath; private NodeRef companyHomeNodeRef; private static Set mutableProperties; + private boolean userNamesAreCaseSensitive = false; + static { Set props = new HashSet(); @@ -99,9 +99,8 @@ public class PersonServiceImpl implements PersonService this.userNamesAreCaseSensitive = userNamesAreCaseSensitive; } - public NodeRef getPerson(String caseSensitiveUserName) + public NodeRef getPerson(String userName) { - String userName = userNamesAreCaseSensitive ? caseSensitiveUserName : caseSensitiveUserName.toLowerCase(); NodeRef personNode = getPersonOrNull(userName); if (personNode == null) { @@ -126,12 +125,12 @@ public class PersonServiceImpl implements PersonService return getPersonOrNull(caseSensitiveUserName) != null; } - public NodeRef getPersonOrNull(String caseSensitiveUserName) + public NodeRef getPersonOrNull(String searchUserName) { - String userName = userNamesAreCaseSensitive ? caseSensitiveUserName : caseSensitiveUserName.toLowerCase(); SearchParameters sp = new SearchParameters(); sp.setLanguage(SearchService.LANGUAGE_LUCENE); - sp.setQuery("TYPE:\\{http\\://www.alfresco.org/model/content/1.0\\}person +@cm\\:userName:\"" + userName + "\""); + sp.setQuery("TYPE:\\{http\\://www.alfresco.org/model/content/1.0\\}person +@cm\\:userName:\"" + searchUserName + + "\""); sp.addStore(storeRef); sp.excludeDataInTheCurrentTransaction(false); @@ -141,22 +140,51 @@ public class PersonServiceImpl implements PersonService { rs = searchService.query(sp); + NodeRef returnRef = null; + for (ResultSetRow row : rs) { NodeRef nodeRef = row.getNodeRef(); if (nodeService.exists(nodeRef)) { - String realUserName = DefaultTypeConverter.INSTANCE.convert( - String.class, - nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME)); - realUserName = userNamesAreCaseSensitive ? realUserName : realUserName.toLowerCase(); - if (realUserName.equals(userName)) + String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty( + nodeRef, ContentModel.PROP_USERNAME)); + + if (userNamesAreCaseSensitive) { - return nodeRef; + if (realUserName.equals(searchUserName)) + { + if (returnRef == null) + { + returnRef = nodeRef; + } + else + { + throw new AlfrescoRuntimeException("Found more than one user for " + searchUserName + + " (case sensitive)"); + } + } + } + else + { + if (realUserName.equalsIgnoreCase(searchUserName)) + { + if (returnRef == null) + { + returnRef = nodeRef; + } + else + { + throw new AlfrescoRuntimeException("Found more than one user for " + searchUserName + + " (case insensitive)"); + } + } } } } + + return returnRef; } finally { @@ -165,8 +193,6 @@ public class PersonServiceImpl implements PersonService rs.close(); } } - - return null; } public boolean createMissingPeople() @@ -179,9 +205,8 @@ public class PersonServiceImpl implements PersonService return mutableProperties; } - public void setPersonProperties(String caseSensitiveUserName, Map properties) + public void setPersonProperties(String userName, Map properties) { - String userName = userNamesAreCaseSensitive ? caseSensitiveUserName : caseSensitiveUserName.toLowerCase(); NodeRef personNode = getPersonOrNull(userName); if (personNode == null) { @@ -195,8 +220,12 @@ public class PersonServiceImpl implements PersonService } } - - properties.put(ContentModel.PROP_USERNAME, userName); + else + { + String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(personNode, + ContentModel.PROP_USERNAME)); + properties.put(ContentModel.PROP_USERNAME, realUserName); + } nodeService.setProperties(personNode, properties); } @@ -231,26 +260,17 @@ public class PersonServiceImpl implements PersonService public NodeRef createPerson(Map properties) { - String caseSensitiveUserName = DefaultTypeConverter.INSTANCE.convert(String.class, properties + String userName = DefaultTypeConverter.INSTANCE.convert(String.class, properties .get(ContentModel.PROP_USERNAME)); - String userName = userNamesAreCaseSensitive ? caseSensitiveUserName : caseSensitiveUserName.toLowerCase(); properties.put(ContentModel.PROP_USERNAME, userName); - return nodeService.createNode( - getPeopleContainer(), - ContentModel.ASSOC_CHILDREN, - ContentModel.TYPE_PERSON, - ContentModel.TYPE_PERSON, - properties).getChildRef(); + return nodeService.createNode(getPeopleContainer(), ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_PERSON, + ContentModel.TYPE_PERSON, properties).getChildRef(); } public NodeRef getPeopleContainer() { NodeRef rootNodeRef = nodeService.getRootNode(storeRef); - List results = searchService.selectNodes( - rootNodeRef, - PEOPLE_FOLDER, - null, - namespacePrefixResolver, + List results = searchService.selectNodes(rootNodeRef, PEOPLE_FOLDER, null, namespacePrefixResolver, false); if (results.size() == 0) { @@ -265,25 +285,22 @@ public class PersonServiceImpl implements PersonService public void deletePerson(String userName) { NodeRef personNodeRef = getPersonOrNull(userName); - + // delete the person if (personNodeRef != null) { nodeService.deleteNode(personNodeRef); } - // translate username based on user name case sensitivity - String authorityName = userNamesAreCaseSensitive ? userName : userName.toLowerCase(); - // remove user from any containing authorities Set containerAuthorities = authorityService.getContainingAuthorities(null, userName, true); for (String containerAuthority : containerAuthorities) { - authorityService.removeAuthority(containerAuthority, authorityName); + authorityService.removeAuthority(containerAuthority, userName); } - + // remove any user permissions - permissionServiceSPI.deletePermissions(authorityName); + permissionServiceSPI.deletePermissions(userName); } public Set getAllPeople() @@ -301,7 +318,6 @@ public class PersonServiceImpl implements PersonService { rs = searchService.query(sp); - for (ResultSetRow row : rs) { @@ -341,7 +357,7 @@ public class PersonServiceImpl implements PersonService { this.permissionServiceSPI = permissionServiceSPI; } - + public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; @@ -377,6 +393,18 @@ public class PersonServiceImpl implements PersonService return companyHomeNodeRef; } + public String getUserIdentifier(String caseSensitiveUserName) + { + NodeRef nodeRef = getPersonOrNull(caseSensitiveUserName); + if ((nodeRef != null) && nodeService.exists(nodeRef)) + { + String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, + ContentModel.PROP_USERNAME)); + return realUserName; + } + return null; + } + // IOC Setters } diff --git a/source/java/org/alfresco/repo/security/person/PersonTest.java b/source/java/org/alfresco/repo/security/person/PersonTest.java index 74afad5525..c898d1cf28 100644 --- a/source/java/org/alfresco/repo/security/person/PersonTest.java +++ b/source/java/org/alfresco/repo/security/person/PersonTest.java @@ -28,6 +28,7 @@ import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.QName; import org.alfresco.util.BaseSpringTest; +import org.alfresco.util.EqualsHelper; public class PersonTest extends BaseSpringTest { @@ -51,8 +52,8 @@ public class PersonTest extends BaseSpringTest StoreRef storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); rootNodeRef = nodeService.getRootNode(storeRef); - - for(NodeRef nodeRef: personService.getAllPeople()) + + for (NodeRef nodeRef : personService.getAllPeople()) { nodeService.deleteNode(nodeRef); } @@ -67,44 +68,40 @@ public class PersonTest extends BaseSpringTest public void xtestPerformance() { personService.setCreateMissingPeople(false); - - personService.createPerson(createDefaultProperties("derek", "Derek", "Hulley", "dh@dh", - "alfresco", rootNodeRef)); - - - + + personService + .createPerson(createDefaultProperties("derek", "Derek", "Hulley", "dh@dh", "alfresco", rootNodeRef)); + long create = 0; - long count = 0; - + long start; long end; - - for(int i = 0; i < 10000; i++) + + for (int i = 0; i < 10000; i++) { - String id = "TestUser-"+i; + String id = "TestUser-" + i; start = System.nanoTime(); - personService.createPerson(createDefaultProperties(id, id, id, id, - id, rootNodeRef)); + personService.createPerson(createDefaultProperties(id, id, id, id, id, rootNodeRef)); end = System.nanoTime(); create += (end - start); - - if((i > 0) && (i % 100 == 0)) + + if ((i > 0) && (i % 100 == 0)) { - System.out.println("Count = "+i); - System.out.println("Average create : "+(create/i/1000000.0f)); + System.out.println("Count = " + i); + System.out.println("Average create : " + (create / i / 1000000.0f)); start = System.nanoTime(); personService.personExists(id); end = System.nanoTime(); - System.out.println("Exists : "+((end-start)/1000000.0f)); - + System.out.println("Exists : " + ((end - start) / 1000000.0f)); + start = System.nanoTime(); int size = personService.getAllPeople().size(); end = System.nanoTime(); - System.out.println("Size ("+size+") : "+((end-start)/1000000.0f)); + System.out.println("Size (" + size + ") : " + ((end - start) / 1000000.0f)); } } } - + public void testCreateMissingPeople1() { personService.setCreateMissingPeople(false); @@ -122,9 +119,9 @@ public class PersonTest extends BaseSpringTest catch (PersonException pe) { - } + } } - + public void testCreateMissingPeople2() { personService.setCreateMissingPeople(false); @@ -137,6 +134,17 @@ public class PersonTest extends BaseSpringTest assertNotNull(nodeRef); testProperties(nodeRef, "andy", "andy", "", "", ""); + nodeRef = personService.getPerson("Andy"); + assertNotNull(nodeRef); + if (personService.getUserIdentifier("Andy").equals("Andy")) + { + testProperties(nodeRef, "Andy", "Andy", "", "", ""); + } + else + { + testProperties(nodeRef, "andy", "andy", "", "", ""); + } + personService.setCreateMissingPeople(false); try { @@ -149,8 +157,7 @@ public class PersonTest extends BaseSpringTest } } - - + public void testCreateMissingPeople() { personService.setCreateMissingPeople(false); @@ -173,7 +180,7 @@ public class PersonTest extends BaseSpringTest assertEquals(2, personService.getAllPeople().size()); assertTrue(personService.getAllPeople().contains(personService.getPerson("andy"))); assertTrue(personService.getAllPeople().contains(personService.getPerson("derek"))); - + } public void testMutableProperties() @@ -184,7 +191,7 @@ public class PersonTest extends BaseSpringTest assertTrue(personService.getMutableProperties().contains(ContentModel.PROP_LASTNAME)); assertTrue(personService.getMutableProperties().contains(ContentModel.PROP_EMAIL)); assertTrue(personService.getMutableProperties().contains(ContentModel.PROP_ORGID)); - + } public void testPersonCRUD1() @@ -200,27 +207,27 @@ public class PersonTest extends BaseSpringTest } } - + public void testPersonCRUD2() { personService.setCreateMissingPeople(false); - personService.createPerson(createDefaultProperties("derek", "Derek", "Hulley", "dh@dh", - "alfresco", rootNodeRef)); + personService + .createPerson(createDefaultProperties("derek", "Derek", "Hulley", "dh@dh", "alfresco", rootNodeRef)); testProperties(personService.getPerson("derek"), "derek", "Derek", "Hulley", "dh@dh", "alfresco"); - + personService.setPersonProperties("derek", createDefaultProperties("derek", "Derek_", "Hulley_", "dh@dh_", - "alfresco_", rootNodeRef)); - + "alfresco_", rootNodeRef)); + testProperties(personService.getPerson("derek"), "derek", "Derek_", "Hulley_", "dh@dh_", "alfresco_"); - + personService.setPersonProperties("derek", createDefaultProperties("derek", "Derek", "Hulley", "dh@dh", "alfresco", rootNodeRef)); - + testProperties(personService.getPerson("derek"), "derek", "Derek", "Hulley", "dh@dh", "alfresco"); - + assertEquals(1, personService.getAllPeople().size()); assertTrue(personService.getAllPeople().contains(personService.getPerson("derek"))); - + personService.deletePerson("derek"); assertEquals(0, personService.getAllPeople().size()); try @@ -233,35 +240,39 @@ public class PersonTest extends BaseSpringTest } } - + public void testPersonCRUD() { personService.setCreateMissingPeople(false); - personService.createPerson(createDefaultProperties("derek", "Derek", "Hulley", "dh@dh", + personService + .createPerson(createDefaultProperties("Derek", "Derek", "Hulley", "dh@dh", "alfresco", rootNodeRef)); + testProperties(personService.getPerson("Derek"), "Derek", "Derek", "Hulley", "dh@dh", "alfresco"); + + personService.setPersonProperties("Derek", createDefaultProperties("derek", "Derek_", "Hulley_", "dh@dh_", + "alfresco_", rootNodeRef)); + + testProperties(personService.getPerson("Derek"), "Derek", "Derek_", "Hulley_", "dh@dh_", "alfresco_"); + + personService.setPersonProperties("Derek", createDefaultProperties("derek", "Derek", "Hulley", "dh@dh", "alfresco", rootNodeRef)); - testProperties(personService.getPerson("derek"), "derek", "Derek", "Hulley", "dh@dh", "alfresco"); - - personService.setPersonProperties("derek", createDefaultProperties("derek", "Derek_", "Hulley_", "dh@dh_", - "alfresco_", rootNodeRef)); - - testProperties(personService.getPerson("derek"), "derek", "Derek_", "Hulley_", "dh@dh_", "alfresco_"); - - personService.setPersonProperties("derek", createDefaultProperties("derek", "Derek", "Hulley", "dh@dh", - "alfresco", rootNodeRef)); - - testProperties(personService.getPerson("derek"), "derek", "Derek", "Hulley", "dh@dh", "alfresco"); - + + testProperties(personService.getPerson("Derek"), "Derek", "Derek", "Hulley", "dh@dh", "alfresco"); + assertEquals(1, personService.getAllPeople().size()); - assertTrue(personService.getAllPeople().contains(personService.getPerson("derek"))); - - personService.deletePerson("derek"); + assertTrue(personService.getAllPeople().contains(personService.getPerson("Derek"))); + assertEquals(personService.personExists("derek"), EqualsHelper.nullSafeEquals(personService.getUserIdentifier("derek"), "Derek")); + assertEquals(personService.personExists("dEREK"), EqualsHelper.nullSafeEquals(personService.getUserIdentifier("dEREK"), "Derek")); + assertEquals(personService.personExists("DEREK"), EqualsHelper.nullSafeEquals(personService.getUserIdentifier("DEREK"), "Derek")); + + personService.deletePerson("Derek"); assertEquals(0, personService.getAllPeople().size()); - + } private void testProperties(NodeRef nodeRef, String userName, String firstName, String lastName, String email, String orgId) { + Map props = nodeService.getProperties(nodeRef); assertEquals(userName, DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME))); assertNotNull(nodeService.getProperty(nodeRef, ContentModel.PROP_HOMEFOLDER)); @@ -287,56 +298,63 @@ public class PersonTest extends BaseSpringTest properties.put(ContentModel.PROP_ORGID, orgId); return properties; } - + public void testCaseSensitive() { - if(personService.getUserNamesAreCaseSensitive()) + + personService + .createPerson(createDefaultProperties("Derek", "Derek", "Hulley", "dh@dh", "alfresco", rootNodeRef)); + + try { - personService.createPerson(createDefaultProperties("Derek", "Derek", "Hulley", "dh@dh", - "alfresco", rootNodeRef)); - - try + NodeRef nodeRef = personService.getPerson("derek"); + if (personService.getUserIdentifier("derek").equals("Derek")) + { + assertNotNull(nodeRef); + } + else { - personService.getPerson("derek"); assertNotNull(null); } - catch (PersonException pe) - { - - } - try - { - personService.getPerson("deRek"); - assertNotNull(null); - } - catch (PersonException pe) - { - - } - try - { - personService.getPerson("DEREK"); - assertNotNull(null); - } - catch (PersonException pe) - { - - } - personService.getPerson("Derek"); } - } - - public void testCaseInsensitive() - { - if(!personService.getUserNamesAreCaseSensitive()) + catch (PersonException pe) { - personService.createPerson(createDefaultProperties("Derek", "Derek", "Hulley", "dh@dh", - "alfresco", rootNodeRef)); - - personService.getPerson("derek"); - personService.getPerson("deRek"); - personService.getPerson("Derek"); - personService.getPerson("DEREK"); + } + try + { + NodeRef nodeRef = personService.getPerson("deRek"); + if (personService.getUserIdentifier("deRek").equals("Derek")) + { + assertNotNull(nodeRef); + } + else + { + assertNotNull(null); + } + } + catch (PersonException pe) + { + + } + try + { + + NodeRef nodeRef = personService.getPerson("DEREK"); + if (personService.getUserIdentifier("DEREK").equals("Derek")) + { + assertNotNull(nodeRef); + } + else + { + assertNotNull(null); + } + } + catch (PersonException pe) + { + + } + personService.getPerson("Derek"); } + } diff --git a/source/java/org/alfresco/service/cmr/security/PersonService.java b/source/java/org/alfresco/service/cmr/security/PersonService.java index 0274a5414a..c985085742 100644 --- a/source/java/org/alfresco/service/cmr/security/PersonService.java +++ b/source/java/org/alfresco/service/cmr/security/PersonService.java @@ -40,12 +40,16 @@ public interface PersonService { /** * Get a person by userName. The person is store in the repository. The - * person may be created as a side effect of this call, depending on - * the setting to {@link #setCreateMissingPeople(boolean) create missing people or not}. + * person may be created as a side effect of this call, depending on the + * setting to + * {@link #setCreateMissingPeople(boolean) create missing people or not}. * - * @param userName - the userName key to find the person + * @param userName - + * the userName key to find the person * @return Returns the person node, either existing or new - * @throws NoSuchPersonException if the user doesn't exist and could not be created automatically + * @throws NoSuchPersonException + * if the user doesn't exist and could not be created + * automatically * * @see #setCreateMissingPeople(boolean) * @see #createMissingPeople() @@ -56,12 +60,13 @@ public interface PersonService /** * Check if a person exists. * - * @param userName the user name + * @param userName + * the user name * @return Returns true if the user exists, otherwise false */ @Auditable(parameters = {"userName"}) public boolean personExists(String userName); - + /** * Does this service create people on demand if they are missing. If this is * true, a call to getPerson() will create a person if they are missing. @@ -74,13 +79,14 @@ public interface PersonService /** * Set if missing people should be created. * - * @param createMissing set to true to create people + * @param createMissing + * set to true to create people * * @see #getPerson(String) */ @Auditable(parameters = {"createMissing"}) public void setCreateMissingPeople(boolean createMissing); - + /** * Get the list of properties that are mutable. Some service may only allow * a limited list of properties to be changed. This may be those persisted @@ -96,8 +102,10 @@ public interface PersonService * Set the properties on a person - some of these may be persisted in * different locations. * - * @param userName - the user for which the properties should be set. - * @param properties - the map of properties to set (as the NodeService) + * @param userName - + * the user for which the properties should be set. + * @param properties - + * the map of properties to set (as the NodeService) */ @Auditable(parameters = {"userName", "properties"}) public void setPersonProperties(String userName, Map properties); @@ -111,9 +119,8 @@ public interface PersonService public boolean isMutable(); /** - * Create a new person with the given properties. - * The userName is one of the properties. - * Users with duplicate userNames are not allowed. + * Create a new person with the given properties. The userName is one of the + * properties. Users with duplicate userNames are not allowed. * * @param properties * @return @@ -128,15 +135,15 @@ public interface PersonService */ @Auditable(parameters = {"userName"}) public void deletePerson(String userName); - + /** * Get all the people we know about. * - * @return a set of people in no specific order. + * @return a set of people in no specific order. */ @Auditable public Set getAllPeople(); - + /** * Return the container that stores people. * @@ -152,4 +159,17 @@ public interface PersonService */ @Auditable public boolean getUserNamesAreCaseSensitive(); + + /** + * Given the case sensitive user name find the approriate identifier from the person service. + * If the system is case sensitive it will return the same string. + * If case insentive it will return the common object. + * If the user does not exist it will return null; + * + * @param caseSensitiveUserName + * @return + */ + + public String getUserIdentifier(String caseSensitiveUserName); + }